HashMap<Integer,String> hashMap = new HashMap<>();
Call constructor, underlying code:
//Expansion factor static final float DEFAULT_LOAD_FACTOR = 0.75f; public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
When called, loadFactor is assigned default_ LOAD_ The value of factor,
Call put
return putVal(hash(key), key, value, false, true);
putVal function
transient Node<K,V>[] table; final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
1. First, enter for the first time and judge if ((tab = table) = = null | (n = tab. Length) = = 0)
At this time, table is empty and tab=table=null, so it is true. Run n = (tab = resize()) length;
The resize() code is as follows:
int threshold; final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> 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; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
oldTab = null; oldCap=0; oldThr=0; newCap = 0; newThr = 0;
Run because oldCap =0
else { // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); }
DEFAULT_INITIAL_CAPACITY=16,DEFAULT_LOAD_FACTOR=0.75.
So newCap = 16; newThr = 12;
Run after
threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; table = newTab;
threshold = 12; Create a new array with a length of 16 And assign it to table Since oldTab is null, the created array is returned directly Complete initialization Return to this step
if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);
Hash is the first parameter of the function, and hash() is called
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
The result is a hash value, and the value of i is a random value of [0,11], which is bound with the hash value, that is, when the key is the same, the hash value is the same, and the random subscript is the same i is the subscript, and the value is p and null. Run to create the initialization node at the hash value After that, it will judge whether the expansion is over
exceptional case
When put has the same key value
When the key is the same, the hash value is the same, and the obtained subscript i is the same. Since the table value in this position is not empty, the else code is as follows:
else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } }
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p;
This part judges whether the Key value of the original node is equal to the Key of the conflicting node or the original node object equals the conflicting object If yes, assign the original node to e, and then call the following code:
if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; }
Give the original value to oldValue. Since onlyIfAbsent is false, e.value = the value of the conflicting node, that is, the value of the original node is' overwritten 'by the value of the conflicting node and the original value is returned
When the Key value is different but the hash value is the same
Run the following code:
else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } }
This is a single linked list structure and assigns a conflict node to the next of the original node. 1. If the next node is empty, directly build the conflict node at the empty position and judge whether the length of the linked list is > = 8. If yes, it will be converted to a red black tree (PS will be converted to a linked list when the red black tree is less than 6.) 2. If the next node is not empty and the 2.1 node is the same as the conflict node, the value is directly passed to the conflict node in the linked list and the old value is returned, 2.2 when the node has no relationship with the conflicting node, assign the next node to the node
Just think of this for the moment