// 擴容兼初始化 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) { // 擴容 if (oldCap >= MAXIMUM_CAPACITY) { // 原數組長度大於最大容量(1073741824) 則將threshold設為Integer.MAX_VALUE=2147483647 // 接近MAXIMUM_CAPACITY的兩倍 threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) { // 新數組長度 是原來的2倍, // 臨界值也擴大為原來2倍 newThr = oldThr << 1; } } else if (oldThr > 0) { // 如果原來的thredshold大於0則將容量設為原來的thredshold // 在第一次帶參數初始化時候會有這種情況 newCap = oldThr; } else { // 在默認無參數初始化會有這種情況 newCap = DEFAULT_INITIAL_CAPACITY;// 16 newThr = (int) (DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);// 0.75*16=12 } if (newThr == 0) { // 如果新 的容量 ==0 float ft = (float) newCap * loadFactor;// loadFactor 哈希加載因子 默認0.75,可在初始化時傳入,16*0.75=12 可以放12個鍵值對 newThr = (newCap < MAXIMUM_CAPACITY && ft < (float) MAXIMUM_CAPACITY ? (int) ft : Integer.MAX_VALUE); } threshold = newThr;// 將臨界值設置為新臨界值 @SuppressWarnings({ "rawtypes", "unchecked" }) // 擴容 Node<K, V>[] newTab = (Node<K, V>[]) new Node[newCap]; table = newTab; // 如果原來的table有數據,則將數據復制到新的table中 if (oldTab != null) { // 根據容量進行循環整個數組,將非空元素進行復制 for (int j = 0; j < oldCap; ++j) { Node<K, V> e; // 獲取數組的第j個元素 if ((e = oldTab[j]) != null) { oldTab[j] = null; // 如果鏈表只有一個,則進行直接賦值 if (e.next == null) // e.hash & (newCap - 1) 確定元素存放位置 newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) //如果原來這個節點已經轉化為紅黑樹了, //那么我們去將樹上的節點rehash之后根據hash值放到新地方 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // 進行鏈表復制 // 方法比較特殊: 它並沒有重新計算元素在數組中的位置 // 而是采用了 原始位置加原數組長度的方法計算得到位置 Node<K, V> loHead = null, loTail = null; Node<K, V> hiHead = null, hiTail = null; Node<K, V> next; do { next = e.next; // 注意:不是(e.hash & (oldCap-1));而是(e.hash & oldCap) // (e.hash & oldCap) 得到的是 元素的在數組中的位置是否需要移動,示例如下 // 示例1: // e.hash=10 0000 1010 // oldCap=16 0001 0000 // & =0 0000 0000 比較高位的第一位 0 //結論:元素位置在擴容后數組中的位置沒有發生改變 // 示例2: // e.hash=17 0001 0001 // oldCap=16 0001 0000 // & =1 0001 0000 比較高位的第一位 1 //結論:元素位置在擴容后數組中的位置發生了改變,新的下標位置是原下標位置+原數組長度 // (e.hash & (oldCap-1)) 得到的是下標位置,示例如下 // e.hash=10 0000 1010 // oldCap-1=15 0000 1111 // & =10 0000 1010 // e.hash=17 0001 0001 // oldCap-1=15 0000 1111 // & =1 0000 0001 //新下標位置 // e.hash=17 0001 0001 // newCap-1=31 0001 1111 newCap=32 // & =17 0001 0001 1+oldCap = 1+16 //元素在重新計算hash之后,因為n變為2倍,那么n-1的mask范圍在高位多1bit(紅色),因此新的index就會發生這樣的變化: // 0000 0001->0001 0001 if ((e.hash & oldCap) == 0) { // 如果原元素位置沒有發生變化 if (loTail == null) loHead = e;// 確定首元素 // 第一次進入時 e -> aa ; loHead-> aa else loTail.next = e; //第二次進入時 loTail-> aa ; e -> bb ; loTail.next -> bb;而loHead和loTail是指向同一塊內存的,所以loHead.next 地址為 bb //第三次進入時 loTail-> bb ; e -> cc ; loTail.next 地址為 cc;loHead.next.next = cc loTail = e; // 第一次進入時 e -> aa ; loTail-> aa loTail指向了和 loHead相同的內存空間 // 第二次進入時 e -> bb ; loTail-> bb loTail指向了和 loTail.next(loHead.next)相同的內存空間 loTail=loTail.next // 第三次進入時 e -> cc ; loTail-> cc loTail指向了和 loTail.next(loHead.next.next)相同的內存 } else { //與上面同理 if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null);//這一塊就是 舊鏈表遷移新鏈表 //總結:1.8中 舊鏈表遷移新鏈表 鏈表元素相對位置沒有變化; 實際是對對象的內存地址進行操作 //在1.7中 舊鏈表遷移新鏈表 如果在新表的數組索引位置相同,則鏈表元素會倒置 if (loTail != null) { loTail.next = null;// 將鏈表的尾節點 的next 設置為空 newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null;// 將鏈表的尾節點 的next 設置為空 newTab[j + oldCap] = hiHead; } } } } } return newTab; }
https://blog.csdn.net/u013494765/article/details/77837338
https://blog.csdn.net/mymilkbottles/article/details/76576367