HashMap
Both the key and value of HashMap are allowed to be null, which is not thread-safe, and there is no guarantee that the internal order of the elements will not change . The underlying data structure adopts an array + a singly linked list/red-black tree , with a bucket array as the main body of the HashMap, and a linked list or red-black tree to resolve hash conflicts.
Next, learn from the JDK source code:
The initial capacity of HashMap is 16, and the default load factor in the constructor is 0.75. When this value is reached, the array will be expanded twice .
You can also pass in the capacity and load factor parameters yourself
扩容机制
:
1. First judge whether the current capacity has reached the maximum capacity, if it is reached, no further expansion.
2. If the current capacity exceeds 16 and the capacity is still less than the maximum capacity after being doubled, the capacity is doubled.
3. If it is a completely new bucket array, assign the default capacity of 16 and the default expansion threshold (16 * 0.75 = 12)
4. If the expansion threshold is 0 (newThr), the expansion is calculated based on the current capacity and load factor Threshold value.
5. Finally build a new bucket array (newTab) according to the capacity obtained above and store the original array in it.
this.threshold = newThr;
HashMap.Node<K, V>[] newTab = new HashMap.Node[newCap];
this.table = newTab;
if (oldTab != null) {
for(int j = 0; j < oldCap; ++j) {
HashMap.Node e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null) {
newTab[e.hash & newCap - 1] = e;
} else if (e instanceof HashMap.TreeNode) {
((HashMap.TreeNode)e).split(this, newTab, j, oldCap);
} else {
HashMap.Node<K, V> loHead = null;
HashMap.Node<K, V> loTail = null;
HashMap.Node<K, V> hiHead = null;
HashMap.Node hiTail = null;
HashMap.Node next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null) {
loHead = e;
} else {
loTail.next = e;
}
loTail = e;
} else {
if (hiTail == null) {
hiHead = e;
} else {
hiTail.next = e;
}
hiTail = e;
}
e = next;
} while(next != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
put(K key, V value)
:
1. Get the hash value of the incoming key.
2. Determine whether the current tab value is null or whether the length is 0. If it is, perform the expansion operation.
3. Obtain the subscript in the table from the hash value through the hash function. If there is no element in the current position, a new node is constructed and stored in the current position.
4. If the key and hash values of the current node are consistent with the passed parameters, they will be overwritten.
5. If the current node location stores the red-black tree, then insert the red-black tree.
6. If the current node location stores a linked list, traverse the node in the fourth step of finding the symbol and cover it, otherwise insert the node at the end of the linked list, and judge whether it needs to be converted to a red-black tree and expansion operation.
get(Object key)
:
The get method is similar to the put method. The corresponding hash value is obtained through the key value, and then converted into an array subscript, and then the position data is traversed and searched.
remove(Object key)
:
1. Determine whether the corresponding subscript of the table and the key value passed in is empty
. 2. Determine whether the first node of the linked list is a search node
. If yes, assign it to node 3. If the current location stores a red-black tree, use it The getTreeNode method finds the node.
4. If it is a linked list, traverse to find the node and assign it to the node.
5. The node is judged to be empty, and at the same time, it is judged whether its value is equal to the incoming parameter, and then deleted.
注意
: The equals method compares the memory addresses of two elements by default, so we need to overwrite the equals method to ensure that the key value will not be repeated.