深究1.8版本HashMap源碼

put方法

    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

 

resize方法

 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

 

 

原創文章,若有什麼不對,歡迎指出。

相關文章
相關標籤/搜索