jdk1.8 HashMap源碼分析(resize函數)


// 擴容兼初始化
    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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM