public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
在putVal方法以前,對 key 進行了 hash 計算。數組
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
經過 hashCode() 方法和無符號左移16後的 hashCode 進行異或計算。 進行了一次擾動計算。app
再看 putVal 方法中。如何計算 key 再數組中的下標。優化
1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 2 boolean evict) { 3 Node<K,V>[] tab; Node<K,V> p; int n, i; 4 if ((tab = table) == null || (n = tab.length) == 0) 5 n = (tab = resize()).length; // 初始擴容 6 if ((p = tab[i = (n - 1) & hash]) == null) 7 tab[i] = newNode(hash, key, value, null); 8 else { 9 Node<K,V> e; K k; 10 if (p.hash == hash && 11 ((k = p.key) == key || (key != null && key.equals(k)))) 12 e = p; 13 else if (p instanceof TreeNode) // 判斷節點是否是紅黑樹節點 14 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); // 加入紅黑樹中 15 else { 16 for (int binCount = 0; ; ++binCount) { 17 if ((e = p.next) == null) { // 將數據加在鏈表的尾部 18 p.next = newNode(hash, key, value, null); 19 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 20 treeifyBin(tab, hash); // 將鏈表轉換成紅黑樹 21 break; 22 } 23 if (e.hash == hash && 24 ((k = e.key) == key || (key != null && key.equals(k)))) 25 break; 26 p = e; 27 } 28 } 29 if (e != null) { // existing mapping for key 30 V oldValue = e.value; 31 if (!onlyIfAbsent || oldValue == null) 32 e.value = value; 33 afterNodeAccess(e); 34 return oldValue; 35 } 36 } 37 ++modCount; 38 if (++size > threshold) 39 resize(); // 擴容 40 afterNodeInsertion(evict); 41 return null; 42 }
在第 6 行中, tab[i = (n - 1) & hash] 計算下標,使用了 hash 值跟 n-1 進行位與運算。this
第一次初始容量爲 16, hash 值跟 15 進行位 與運算。而 15 的二進制是 1111。獲得的是 hash 值的前 4位二進制。因此獲得的結果就是,0-15之間的數字,正好是數組下標。spa
假如容量已經擴展,那如今的容量爲 2n,hash 值跟 2n-1 進行位 與運算, 獲得的是 hash 值的前 n 位二進制。code
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 (oldCap >= MAXIMUM_CAPACITY) { // 超過最大值,再也不擴容,隨它怎麼碰撞 8 threshold = Integer.MAX_VALUE; 9 return oldTab; 10 } 11 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && 12 oldCap >= DEFAULT_INITIAL_CAPACITY) 13 newThr = oldThr << 1; // double threshold // 不然,將原數組大小擴充一倍 14 } 15 else if (oldThr > 0) // initial capacity was placed in threshold 16 newCap = oldThr; 17 else { // zero initial threshold signifies using defaults 18 newCap = DEFAULT_INITIAL_CAPACITY; 19 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); 20 } 21 if (newThr == 0) { 22 float ft = (float)newCap * loadFactor; 23 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? 24 (int)ft : Integer.MAX_VALUE); 25 } 26 threshold = newThr; 27 @SuppressWarnings({"rawtypes","unchecked"}) 28 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 29 table = newTab; 30 if (oldTab != null) { 31 for (int j = 0; j < oldCap; ++j) { 32 Node<K,V> e; 33 if ((e = oldTab[j]) != null) { 34 oldTab[j] = null; 35 if (e.next == null) 36 newTab[e.hash & (newCap - 1)] = e; 37 else if (e instanceof TreeNode) 38 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); 39 else { // preserve order 40 Node<K,V> loHead = null, loTail = null; 41 Node<K,V> hiHead = null, hiTail = null; 42 Node<K,V> next; 43 do { 44 next = e.next; 45 if ((e.hash & oldCap) == 0) { // 將hash 與 16 這種 2n 進行 與操做,計算新增的一位bit 是0,仍是 1。 46 if (loTail == null) 47 loHead = e; 48 else 49 loTail.next = e; 50 loTail = e; 51 } 52 else { 53 if (hiTail == null) 54 hiHead = e; 55 else 56 hiTail.next = e; 57 hiTail = e; 58 } 59 } while ((e = next) != null); 60 if (loTail != null) { // 0 該節點還存原下標位置 61 loTail.next = null; 62 newTab[j] = loHead; 63 } 64 if (hiTail != null) { // 1 該節點由原下標位置向後移動 2n 位置 65 hiTail.next = null; 66 newTab[j + oldCap] = hiHead; 67 } 68 } 69 } 70 } 71 } 72 return newTab; 73 }
新版 hashMap 對擴容進行了優化,當容量擴充爲原來的 2 倍時,只需判斷新增的一位 bit 是0仍是1。blog
例如,當由 16 擴充至 32 時,16 的 二進制時 10000,與 hash 計算,獲得的是第 5 位 bit 的值。ci
本來計算下標,只是與前 4 位進行與運算,如今擴容一次後是對前 5 位進行與運算。hash
擴容方法裏面並無從新計算全部位的與運算,只需判斷新增的第 5 位。減小了計算量。it
原創文章,若有什麼不對,歡迎指出。