title | category | tag | head | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Summary of Common Java Collection Interview Questions (Part 2) |
Java |
|
|
- Thread Safety:
HashMap
is not thread-safe, whileHashtable
is thread-safe because most of its internal methods are synchronized. (If you need to ensure thread safety, useConcurrentHashMap
!) - Efficiency: Due to thread safety issues,
HashMap
is generally more efficient thanHashtable
. Additionally,Hashtable
is largely obsolete and should not be used in code. - Support for Null Keys and Values:
HashMap
can store null keys and values, but only one null key is allowed, while multiple null values can be stored.Hashtable
does not allow null keys or values, and will throw aNullPointerException
if attempted. - Initial Capacity and Growth Size Differences: ① If no initial capacity is specified during creation,
Hashtable
defaults to an initial size of 11, and each subsequent growth increases the capacity to 2n+1.HashMap
defaults to an initial size of 16, and each subsequent growth doubles the capacity. ② If an initial capacity is specified,Hashtable
uses that size directly, whileHashMap
expands it to the next power of 2 (thetableSizeFor()
method inHashMap
ensures this, and the source code is provided below). This meansHashMap
always uses powers of 2 for the hash table size, which will be explained later. - Underlying Data Structure: After JDK 1.8,
HashMap
has undergone significant changes in handling hash collisions. When the length of a linked list exceeds a threshold (default is 8), it converts the list into a red-black tree (before converting to a red-black tree, it checks if the current array length is less than 64; if so, it opts for array expansion instead of conversion to a red-black tree) to reduce search time (this process will be analyzed in conjunction with the source code later).Hashtable
does not have such a mechanism. - Hash Function Implementation:
HashMap
applies a high and low bit mixing disturbance to the hash value to reduce collisions, whileHashtable
directly uses the key'shashCode()
value.
Constructor of HashMap
with Initial Capacity:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
The following method ensures that HashMap
always uses powers of 2 for the hash table size.
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
If you have looked at the source code of HashSet
, you should know that HashSet
is implemented based on HashMap
. (The source code of HashSet
is very minimal because, apart from clone()
, writeObject()
, and readObject()
, which HashSet
must implement, all other methods directly call methods from HashMap
.)
HashMap |
HashSet |
---|---|
Implements Map interface |
Implements Set interface |
Stores key-value pairs | Stores only objects |
Calls put() to add elements to the map |
Calls add() method to add elements to the Set |
`HashMap |