Put Method of HashMap
The data structure design of HashMap can be referred to. link . Next, we review the put(Key k, Value v) process of HashMap:
(1) Hash value is calculated for Key, hash table subscript is calculated, corresponding to hashCode() method, so when class object is used as Key, the hashCode() method and equals() method of the object need to be rewritten.
(2) If there is no collision, put it directly into the bucket, that is, the linked list header of the corresponding position of the Hash table array.
(3) If the collision occurs, replace the old value if the node already exists, otherwise link the element to the back in a linked list.
(4) If the length of the list exceeds the threshold (TREEIFY_THRESHOLD=== 8), the list is converted to a red-black tree. I'm not familiar with mangrove trees. I'm not going to talk about them here.
(5) If the bucket is full (capacity * load factor), resize is required.
Extension mechanism of HashMap
Assuming that length is the size of an array of Hash tables, the method indexFor(int hash, int length) is
indexFor(int hash, int length) { return hash % length; }
In the resize process, elements on the same Entry chain in the old array may be placed in different positions of the new array by recalculating the index position. JDK8 has some optimizations. In resize process, the modification of the array size of the Hash table uses an expansion of the second power (i.e. the length is twice as long as the original), which has two advantages.
Benefits 1
In the source code of hashmap. The put method calls the indexFor(int h, int length) method, which finds the entry's position in the Hash table array based on the hash value of the key. The source code is as follows:
/** * Returns index for hash code h. */ static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1); }
The above code is also equivalent to finding the module for length. Notice that the final return is H & (length-1). If length is not a power of 2, say 15. Then the binary of length-1 becomes 1110. In the case that h is a random number, do & operate with 1100. The last number is always zero. Then the places with the last number of 1, such as 0001, 1001, 1101, can never be occupied by entry. This will lead to waste, not random and other issues. The more digits 1 in length-1 binary, the average distribution.
Benefits 2
The following figure is taken as a n example, in which graph (a) represents an example of key1 and key2 determining index positions before expansion, and graph (b) represents an example of key1 and key2 determining index positions after expansion, and N represents length.
After the element recalculates hash, because n becomes twice as large, the mask range of n-1 is more than 1 bit (red), so the new index will change as follows:
There is no need to recalculate hash in resize process like the implementation of JDK 1.7. Just look at whether the bit added to the original hash value is 1 or 0. If it is 0, the index will remain unchanged. If it is 1, the index will become "original index + oldCap". You can see the resize schematic diagram expanded from 16 to 32 (on the one hand, bit operation is faster, on the other hand, the collision-resistant Hash function is time-consuming). :
Source code is as follows
1 final Node<K,V>[] resize() { 2 Node<K,V>[] oldTab = table; 3 int oldCap = (oldTab == null) ? 0 : oldTab.length; 4 int oldThr = threshold; 5 int newCap, newThr = 0; 6 if (oldCap > 0) { 7 // If you exceed the maximum, you will not expand any more, so you have to collide with it. 8 if (oldCap >= MAXIMUM_CAPACITY) { 9 threshold = Integer.MAX_VALUE; 10 return oldTab; 11 } 12 // If it does not exceed the maximum value, it will be expanded to twice the original value. 13 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 14 oldCap >= DEFAULT_INITIAL_CAPACITY) 15 newThr = oldThr << 1; // double threshold 16 } 17 else if (oldThr > 0) // initial capacity was placed in threshold 18 newCap = oldThr; 19 else { // zero initial threshold signifies using defaults 20 newCap = DEFAULT_INITIAL_CAPACITY; 21 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 22 } 23 // Calculating the New resize Upper Limit 24 if (newThr == 0) { 25 26 float ft = (float)newCap * loadFactor; 27 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 28 (int)ft : Integer.MAX_VALUE); 29 } 30 threshold = newThr; 31 @SuppressWarnings({"rawtypes","unchecked"}) 32 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 33 table = newTab; 34 if (oldTab != null) { 35 // Move each bucket to the new buckets 36 for (int j = 0; j < oldCap; ++j) { 37 Node<K,V> e; 38 if ((e = oldTab[j]) != null) { 39 oldTab[j] = null; 40 if (e.next == null) 41 newTab[e.hash & (newCap - 1)] = e; 42 else if (e instanceof TreeNode) 43 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 44 else { // Chain list optimization heavy hash block 45 Node<K,V> loHead = null, loTail = null; 46 Node<K,V> hiHead = null, hiTail = null; 47 Node<K,V> next; 48 do { 49 next = e.next; 50 // Original reference 51 if ((e.hash & oldCap) == 0) { 52 if (loTail == null) 53 loHead = e; 54 else 55 loTail.next = e; 56 loTail = e; 57 } 58 // Original Index + oldCap 59 else { 60 if (hiTail == null) 61 hiHead = e; 62 else 63 hiTail.next = e; 64 hiTail = e; 65 } 66 } while ((e = next) != null); 67 // Place the original index in the bucket 68 if (loTail != null) { 69 loTail.next = null; 70 newTab[j] = loHead; 71 } 72 // The original index + oldCap is put in the bucket 73 if (hiTail != null) { 74 hiTail.next = null; 75 newTab[j + oldCap] = hiHead; 76 } 77 } 78 } 79 } 80 } 81 return newTab; 82 }
Reference
https://zhidao.baidu.com/question/1738414783693877787.html
https://blog.csdn.net/aichuanwendang/article/details/53317351