/** * 這個方法是基於當前桶中全部元素的數量進行計算的使用閾值爲threshold.它不一樣於鏈表轉化到tree時的鏈表長度(能夠理解爲樹的高度)閾值TREEIFY_THRESHOLD */ final Node<K,V>[] resize() { //----------------------------------- 新容量與閾值計算 ----------------------------------- // 緩存桶引用 Node<K,V>[] oldTab = table; // 緩存老的桶的長度,桶爲null時,使用0 // 注意,這裏用的是oldTab.length,而不是size int oldCap = (oldTab == null) ? 0 : oldTab.length; // 緩存閾值 int oldThr = threshold; // 新桶容量與閾值 int newCap, newThr = 0; // 老容量大於.這通常表明這個桶已經通過了resize的數次處理 if (oldCap > 0) { // 老容量大於MAXIMUM_CAPACITY = 1 << 30 = 1073741824 // 容量計算方式爲n<<1,當oldCap >= MAXIMUM_CAPACITY時,再次執行位移.其可能的最大值就是Integer.MAX_VALUE if (oldCap >= MAXIMUM_CAPACITY) { // 設置閾值爲Integer.MAX_VALUE threshold = Integer.MAX_VALUE; // 直接return.放棄所有後續處理 return oldTab; } // 使用oldCap << 1初始化newCap // 當oldCap小於MAXIMUM_CAPACITY而且oldCap大於DEFAULT_INITIAL_CAPACITY(16)時 // 此時newCap可能已經大於MAXIMUM_CAPACITY而且newThr=0或者newCap很小(小於16>>2)而且newThr=0 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) //設置newThr爲oldThr << 1(這裏沒有作正確性校驗,待查) newThr = oldThr << 1; // double threshold } // 判斷老閾值是否大於0 // 走到這說明oldCap==0,而且使用了包含initialCapacity參數的構造器構造了這個map,且沒有被添加過元素 else if (oldThr > 0) // initial capacity was placed in threshold // 使用將新容量複製爲老閾值(newCap此時爲0) // 注意: 在使用了包含initialCapacity參數的構造方法時,其threshold已經被計算爲2的n次冪 newCap = oldThr; else { // zero initial threshold signifies using defaults // 默認方法,當使用無參構造方法時,會出現oldThr與oldCap都等於0的狀況 // 使用默認初始化容量賦值到newCap newCap = DEFAULT_INITIAL_CAPACITY; // 使用默認初始化容量與加載因子相乘賦值到newThr newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } // 統一處理newThr if (newThr == 0) { // 新容量與加載因子相乘 float ft = (float)newCap * loadFactor; // 當newCap與ft均小於MAXIMUM_CAPACITY時,newThr=ft.不然newThr=Integer.MAX_VALUE newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } //----------------------------------- 元素重排 ----------------------------------- // 更新threshold 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; // 進行桶元素獲取 // 判斷桶元素是否存在(由於使用(n-1)&hash的方式進行計算,因此常常會出現這種狀況) if ((e = oldTab[j]) != null) { // 刪除引用 oldTab[j] = null; // 判斷桶元素是否有下一個元素 if (e.next == null) // 沒有下一個元需.使用相同的算法計算在新桶中的下標並賦值 newTab[e.hash & (newCap - 1)] = e; // 桶元素存在next,判斷是否爲TreeNode else if (e instanceof TreeNode) // 進行委派執行 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order // 對於鏈表結構,拆分到高位與低位兩組 // loHead與loTail非別表明低位頭與低位尾 Node<K,V> loHead = null, loTail = null; // hiHead與hiTail非別表明高位頭與高位尾 Node<K,V> hiHead = null, hiTail = null; // next Node<K,V> next; // 已經存在遍歷目標,直接使用do while do { // 拿到e的next. next = e.next; // 判斷e的hash是不是高位 // 判斷原理以下. // 首先oldCap恆定爲2的n次冪,二進制表達爲1000... // 下標計算方程爲(n-1)&hash // 帶入n後,爲...111&hash // 當n=111時,hash爲1101,結果爲101 // 當n=1111時,hash爲1101,結果爲1101.表示爲高位(注意hash的高位) // 當n=1111時,hash爲0101,結果爲101.表示爲低位(注意hash的高位) // 這樣就,能夠直接求出新的下標.可是,這種方式須要對全部的元素進行從新計算,很是低效 // 因此jdk使用了一個特別的方法.就是直接比較最高位,當一個hash與數組長度(也就是n的n次冪)時,如1101&1000 // 當結果等於0時,表明這個hash是低位hash,其餘就是高位hash if ((e.hash & oldCap) == 0) { // 低位 // 判斷低位尾部是否存在 if (loTail == null) // 不存在,表明頭部也沒有,進行初始化 loHead = e; else // 存在,追加到尾部的next loTail.next = e; // 更新尾部 loTail = e; } else { // 高位 if (hiTail == null) // 不存在,表明頭部也沒有,進行初始化 hiHead = e; else // 存在,追加到尾部的next hiTail.next = e; // 更新尾部 hiTail = e; } } while ((e = next) != null); // 進行收尾處理 // 判斷低位是否爲空 if (loTail != null) { // 不爲空 // 清除末尾元素的next.當loTail是鏈表倒數第二個元素且倒數第一個元素是高位元素時,須要清空loTail的next對高位元素的引用 loTail.next = null; // 低位使用原下標進行保存 newTab[j] = loHead; } if (hiTail != null) { // 不爲空 // 清除末尾元素的next.理由同上但判斷方式相反 hiTail.next = null; // 低位使用原下標+原容量進行保存 newTab[j + oldCap] = hiHead; } } } } } // 返回newTab return newTab; }