hashmap和concurrenthashmap原始碼分析(1.7/1.8)

專注的阿熊發表於2021-07-27

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) {

        // 1 判斷老表的容量是否超過最大容量值:如果超過則將閾值設定為 Integer.MAX_VALUE ,並直接返回

      // 此時 oldCap * 2 Integer.MAX_VALUE 大,因此無法進行重新分佈,只是單純的將閾值擴容到最大

         if (oldCap >= MAXIMUM_CAPACITY) {

             threshold = Integer.MAX_VALUE;

             return oldTab;

         }

       // newCap 賦值為 oldCap 2 倍,如果 newCap< 最大容量並且 oldCap>=16, 則將新閾值設定為原來的兩倍

         else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY)

             newThr = oldThr << 1; // double threshold 24

     }

     //2. 如果老表的容量為 0, 老表的閾值大於 0, 是因為初始容量被放入閾值,則將新表的容量設定為老表的閾值

     else if (oldThr > 0)

         newCap = oldThr;

     else {

         //3. 老表的容量為 0, 老表的閾值為 0 ,這種情況是沒有傳初始容量的 new 方法建立的空表,將閾值和容量設定為預設值。

         newCap = DEFAULT_INITIAL_CAPACITY;

         newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

     }

     // 4. 如果新表的閾值為空 , 則透過新的容量 * 負載因子獲得閾值

     if (newThr == 0) {

         float ft = (float)newCap * loadFactor;

         newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE);

     }

   // 將當前閾值設定為剛計算出來的新的閾值,定義新表,容量為剛計算出來的新容量,將 table 設定為新定義的表。

     threshold = newThr;

     @SuppressWarnings({"rawtypes","unchecked"})

     Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];

     table = newTab;

   // 6. 如果老表不為空,則需遍歷所有節點,將節點賦值給新表

     if (oldTab != null) {

         // 把每個 bucket 都移動到新的 buckets

         for (int j = 0; j < oldCap; ++j) {

             Node<K,V> e;

             if ((e = oldTab[j]) != null) {   // 將索引值為 j 的老表頭節點賦值給 e

                 oldTab[j] = null;         // 將老表的節點設定為空 , 以便垃圾收集器回收空間

   //7. 如果 e.next 為空 , 則代表老表的該位置只有 1 個節點,外匯跟單gendan5.com計算新表的索引位置 , 直接將該節點放在該位置

                 if (e.next == null)

                     newTab[e.hash & (newCap - 1)] = e;

    //8. 如果是紅黑樹節點,則進行紅黑樹的重 hash 分佈 ( 跟連結串列的 hash 分佈基本相同 )

                 else if (e instanceof TreeNode)

                     ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);

                 else {

     // 9. 如果是普通的連結串列節點,則進行普通的重 hash 分佈

                     Node<K,V> loHead = null, loTail = null;// 儲存索引位置為 : “原索引位置”的節點

                     Node<K,V> hiHead = null, hiTail = null;// 儲存索引位置為 : “原索引位置 +oldCap ”的節點

                     Node<K,V> next;

                     do {

                         next = e.next;

           // 如果 e hash 值與老表的容量進行與運算為 0, 則擴容後的索引位置跟老表的索引位置一樣

                         if ((e.hash & oldCap) == 0) {

                             if (loTail == null)  // 如果 loTail 為空 , 代表該節點為第一個節點                             

                                 loHead = e;     // 則將 loHead 賦值為第一個節點

                             else

                                 loTail.next = e; // 否則將節點新增在 loTail 後面                            

                             loTail = e;   // 並將 loTail 賦值為新增的節點

                         }

              // 如果 e hash 值與老表的容量進行與運算為非 0, 則擴容後的索引位置為 : 老表的索引位置+ oldCap                        

                         else {

                             if (hiTail == null)  // 如果 hiTail 為空 , 代表該節點為第一個節點                              

                                 hiHead = e;    // 則將 hiHead 賦值為第一個節點

                             else

                                 hiTail.next = e;   // 否則將節點新增在 hiTail 後面

                             hiTail = e;      // 並將 hiTail 賦值為新增的節點

                         }

                     } while ((e = next) != null);                     

        // 10. 如果 loTail 不為空(說明老表的資料有分佈到新表上“原索引位置”的節點),則將最後一個節點的 next 設為空,並將新表上索引位置為“原索引位置”的節點設定為對應的頭節點                   

                     if (loTail != null) {

                         loTail.next = null;

                         newTab[j] = loHead;

                     }

                 // 11. 如果 hiTail 不為空(說明老表的資料有分佈到新表上“原索引 +oldCap 位置”的節點),則將最後一個節點的 next 設為空,並將新表上索引位置為“原索引 +oldCap ”的節點設定為對應的頭節點

                     if (hiTail != null) {

                         hiTail.next = null;

                         newTab[j + oldCap] = hiHead;

                     }

                 }

             }

         }

     }

        //12. 返回新表

     return newTab;

}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69946337/viewspace-2783627/,如需轉載,請註明出處,否則將追究法律責任。

相關文章