hashMap傳入參數,table長度爲多少

前言


個人全部文章同步更新與Github--Java-Notes,想了解JVM,HashMap源碼分析,spring相關,劍指offer題解(Java版),能夠點個star。能夠看個人github主頁,天天都在更新喲。html

邀請您跟我一同完成 repojava


建立函數時,傳入初始長度0,1,2,3,4……15,16,數組table長度爲多少

記住一點,當table進行初始化的時候,table.length 就是 比傳入的值大的或者等於的最小的 2的n次方table.length 的長度一直是 2的n次方git

也就是說,我new HashMap(0),table初始化後 table.length ==1(固然,源碼中全部的變量都採用延遲初始化,只有等到用的時候,即put元素的時候才初始化。若是沒有放入元素,那麼 table一直爲 null,我上面的只是另一個變量 threshold得出來的,由於這兩個有關係。我使用斷點調試一下就知道了)github

咱們注意到,當構建函數時,threshold的初始值和 tableSizeFor()這個函數有關spring

咱們再進入 tableSizeFor

這個是返回一個比輸入值大的或者等於的最小的 2的n次方(若是你不明白能夠看下我後面的測試值和這篇文章數組

www.cnblogs.com/loading4/p/…函數

因此他返回的值爲 1(比0大的或者等於的最小的2的n次方 就是1);源碼分析

而後這個時候假如咱們第一次 put一個元素,這個時候table數組就要開始初始化了,他就會執行這個resize函數,在源碼中是這樣測試

resize的解讀以下spa

/** * 擴容函數 * 使用情景: 1.初始化數組table, * 2.++size>threshold. * @return 新的數組table * 或者是當擴容一倍長度超過最大值,返回原來的table數組 */
    final  Entry[] resize() {
        // 定義舊的數組爲 Entry 類型的數組,oldTab
        Entry[] oldTab = table;
        // 若是oldTab==null 則返回 0,不然返回數組大小
        int oldCap = (oldTab==null) ? 0 : oldTab.length;

        int oldThreshold = threshold;

        int newCap,newThreshold=0;

        // 說明已經不是第一次 擴容,那麼已經初始化過,容量必定是 2的n次方,因此能夠直接位運算
        if(oldCap>0){
            // 若是 原來的數組大小已經大於等於了最大值,那麼閾值設置爲 Integer的最大值,即不會再進行擴容
            if(oldCap >= MAX_CAPACITY){
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }

            // 所以已經不是第一次擴容,必定是2的n次方
            else if ((newCap = oldCap << 1) < MAX_CAPACITY &&
                      oldCap >= INIT_CAPACITY)

                newThreshold = oldThreshold << 1;

        }
        // 若是oldThreshold > 0,而且oldCap == 0,說明是尚未進行調用resize方法。
        // 說明輸入了初始值,且oldThreshold爲 比輸入值大的最小的2的n次方
        // 那麼就把 oldThreshold 的值賦給 newCap ,由於這個值如今爲 比輸入值大的最小的2的n次方
        else if(oldThreshold>0)
            newCap = oldThreshold;

        // 這個是隻有使用無參構造器的時候才能知足的條件。,所有是否默認的值
        else{
            newCap = INIT_CAPACITY;
            newThreshold = (int) (INIT_CAPACITY * DEFAULT_LOADFACTOR);
        }

        //
        if(newThreshold == 0){

            float ft = (float) (newCap * loadFactor);
            newThreshold =(newCap < MAX_CAPACITY && ft < (float) MAX_CAPACITY ?
                    (int )ft : Integer.MAX_VALUE);
        }

        threshold = newThreshold;

        Entry newTable[] = new Entry[newCap];
        table=newTable;

        // 將原來數組中的全部元素都 copy進新的數組
        if(oldTab != null){
            for (int j = 0; j < oldCap; j++) {
                Entry e;

                if((e = oldTab[j]) != null){
                    oldTab[j] = null;

                    // 說明尚未成鏈,數組上只有一個
                    if(e.next == null){
                        // 從新計算 數組索引 值
                        newTable[e.h & (newCap-1)] = e;

                    }
                    // 判斷是否爲樹結構
                    //else if (e instanceof TreeNode)


                    // 若是不是樹,只是鏈表,即長度尚未大於 8 進化成樹
                    else{
                        // 擴容後,若是元素的 index 仍是原來的。就使用這個lo前綴的
                        Entry loHead=null, loTail =null;

                        // 擴容後 元素index改變,那麼就使用 hi前綴開頭的
                        Entry hiHead = null, hiTail = null;
                        Entry next;
                        do {
                            next = e.next;
                            //這個很是重要,也比較難懂,
                            // 將它和原來的長度進行相與,就是判斷他的原來的hash的上一個 bit 位是否爲 1。
                            //以此來判斷他是在相同的索引仍是table長度加上原來的索引
                            if((e.h & oldCap) == 0){
                                // 若是 loTail == null ,說明這個 位置上是第一次添加,沒有哈希衝突
                                if(loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else{
                                if(hiTail == null)
                                    loHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e ;
                            }

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


                        if(loTail != null){
                            loTail.next = null;
                            newTable[j] = loHead;
                        }

                        // 新的index 等於原來的 index+oldCap
                        else {

                            hiTail.next = null;
                            newTable[j+oldCap] = hiHead;
                        }

                    }
                }

            }
        }

        return newTable;
    }

複製代碼

咱們注意這裏,知足這個的條件爲 oldCap==0 && oldThreshold>0

else if(oldThreshold>0)
        newCap = oldThreshold;
複製代碼

oldThreshold等於以前使用 tableSizeFor 的返回值,也就是 1;因此table一經初始化長度就爲1**(可是還要說明一點,執行完 resize函數以後 table.length等於1,threshold等於0。看我上面的分析,newThreshold=(int)0.75)就等於0**

可是咱們執行的是存入元素的操做,因此存完以後,就要++size(由於以前裏面沒有元素),這個時候 size就等於1,他就大於 threshold。因此他又要執行resize函數進行擴容操做。執行完以後,table.length 就等於2了,threshold就等於 1了。

table的擴容所有都是乘以 2(左移一位),並且table.length也一直等於2的n次方,即(table.length &(table.length-1)) == 0

以後同理能夠推,一開始建立對象時,傳入 1的話,table 初始化長度爲1;傳入 2,長度爲2;傳入3,長度爲4;傳入[5,8],長度爲8;傳入[9,16],長度爲16

相關文章
相關標籤/搜索