HashMap之tableSizeFor(int cap)方法原理詳解(分2的n次冪和非2的n次冪兩種情況討論)

方法說明:HashMap的tableSizeFor(int cap)方法,可以返回一個大於或等於給定cap值的且最靠近cap值的2的n次冪的數值.此方法可以保證HashMap的數組容量一定是2的n次冪.採用的具體算法原理詳細如下:
原理1:二進制或運算:0|0=0 0|1=1 1|1=1,只要有1結果就等於1.
原理2:假設某個int 正數,其二進制表達式(記爲A)中1所在最高位的位置是從左往右數第n個數(2≤n≤32,因爲int佔4個字節,4個字節等於32個比特位,所以n最大爲32;而當n=0時,二進制表達式中無1,無符號右移後也沒任何變化,故不做討論;當n=1時,二進制表達式中只有1個1,故再如何無符號右移後再或運算也都不會有任何變化,故也不做討論),
原理3:某int正數二進制表達式中1所在最高位的位置是從左往右數第n位,則能換算成1的位數最多也只能有n位.
步驟1:則A無符號右移1位後與原A做或位運算得到二進制表達式B,可保證B前2個高位全是1;(前提1:32≥n≥2)
步驟2:則B無符號右移2位後與原B做或位運算得到二進制表達式C,可保證C前4個高位全是1;(前提2:32≥n≥4)
步驟3:則C無符號右移4位後與原C做或位運算得到二進制表達式D,可保證D前8個高位全是1;(前提3:32≥n≥8)
步驟4:則D無符號右移8位後與原D做或位運算得到二進制表達式E,可保證E前16個高位全是1;(前提4:32≥n≥16)
步驟5:則E無符號右移16位後與原E做或位運算得到二進制表達式F,可保證F前32個高位全是1;(前提5:n=32)
注意:1.上述步驟是有前後順序的,是一步步從左到右把位數全部換算位1的.且只有滿足對應的前提條件,才能得到對應的保證結果;
2.若1所在最高位的位置是2時,其實走到步驟1就已經結束了,後續步驟再如何無符號右移後再或運算,其最終結果也都不會有任何變化了,因爲最高位的位置限定了其能有的1的個數.
3.若還未走到步驟5時,最高位後面的位就已經都換算成了1,則後續步驟也都會照常進行下去,但並不會對最終結果有任何影響,道理同第2點.

 /**
     * Returns a power of two size for the given target capacity.
     */
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        //第一點:這裏減1,是爲了保證本身已經是2的n次冪的情形下(如:2^3=8),直接返回該值,而不是返回另外的臨近的大於它的其他2的n次冪的值(2^4=16)
        //舉例:
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        //第二點:經過上述的無符號右移和或位運算的操作,會將最高位1後面的位全變爲1,
        //舉例:cap=25,應該返回32
        //32=2^5=( 2^0+ 2^1+ 2^2+ 2^3+ 2^4)+1
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
        //第三點:此處的n+1結合第二點,可以保證返回的是2的n次冪的數值
    }

舉例1 本身就是2的n次冪:
假設cap=256,期望得到的結果應該是256=1×2^8= (1×2^0+ 1×2^1+ 1×2^2+ 1×2^3+ 1×2^4+ 1×2^5+ 1×2^6+ 1×2^7)+1(即將1所在的最高位後面的位的值全部換算爲0,然後對最高位後的所有位求和後再加1),tableSizeFor(int cap)方法的詳細運算過程如下:
static final int tableSizeFor(int cap) {//cap=256
第一步:int n = cap - 1;//n=256-1=255=1+2+4+8+16+32+64+128=1×2^0+ 1×21+1×22+ 1×2^3+ 1×2^4+ 1×2^5+ 1×2^6+ 1×2^7
//255是正數,所以其原碼/反碼/補碼對應的二進制表示都是一樣的,且一個int等於4個字節等於32比特,所以其二進制完整表示等於00000000 00000000 00000000 11111111
//下面這段計算,一開始粗心了,真沒看懂,還以爲是個什麼新的計算符號,用心瞧了瞧,其實很簡單
第二步:n|=n>>>1;//即n=n|(n>>>1);即此時n的二進制值 或位運算 此時n的二進制值無符號右移1位後的值

此時n的二進制值							00000000 00000000 00000000 11111111
  										或運算
此時n的二進制值無符號右移1位後的值			00000000 00000000 00000000 01000100
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11000100  

結論:由於最高位是第八位,且此時最高位後續的位的0都已經換算成了1,即前8位都是1了,即使再無符號右移了8位,然後再與前8位已經是1的值進行或的位運算,結果還是不變的.

第三步:n|=n>>>2;//即n=n|(n>>>2);即此時n的二進制值 或位運算 此時n的二進制值無符號右移2位後的值

此時n的二進制值							00000000 00000000 00000000 11000100
  										或運算
此時n的二進制值無符號右移2位後的值			00000000 00000000 00000000 00110001
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11110101  

結論:主要是將前2個已經爲1的最高位向右移了2位,然後再與前2個2最高位是1的值進行或的位運算時,就可以保證前4位都是1
第四步:n|=n>>>4;//即n=n|(n>>>4);即此時n的二進制值 或位運算 此時n的二進制值無符號右移4位後的值

此時n的二進制值							00000000 00000000 00000000 11110101  
  										或運算
此時n的二進制值無符號右移4位後的值			00000000 00000000 00000000 00001111
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11111111  

結論:主要是將前4個已經爲1的最高位向右移了4位,然後再與前4個最高位是1的值進行或的位運算時,就可以保證前8位都是1
注意:此時已經達到除高位1外,其他位都換算成1的目的了,所以後續的步驟的處理邏輯應該只是保持該結果,而不應該再對該結果有所變更.
第五步:n|=n>>>8;//即n=n|(n>>>8);即此時n的二進制值 或位運算 此時n的二進制值無符號右移8位後的值

此時n的二進制值							00000000 00000000 00000000 11111111  
  										或運算
此時n的二進制值無符號右移8位後的值			00000000 00000000 00000000 00000000
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11111111  

結論:由於最高位是第八位,且此時最高位後續的位的0都已經換算成了1,即前8位都是1了,即使再無符號右移了8位,然後再與前8位已經是1的值進行或的位運算,結果還是不變的.
第六步:n|=n>>>16;//即n=n|(n>>>16);即此時n的二進制值 或位運算 此時n的二進制值無符號右移16位後的值

此時n的二進制值							00000000 00000000 00000000 11111111  
  										或運算
此時n的二進制值無符號右移8位後的值			00000000 00000000 00000000 00000000
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11111111  

結論:同第五步的結論,由於最高位是第八位,且此時最高位後續的位的0都已經換算成了1,即前8位都是1了,即使再無符號右移了16位,然後再與前8位已經是1的值進行或的位運算,結果還是不變的.
}

舉例2 本身不是2的n次冪:
假設cap=137,期望得到的結果應該是256=1×2^8= (1×2^0+ 1×2^1+ 1×2^2+ 1×2^3+ 1×2^4+ 1×2^5+ 1×2^6+ 1×2^7)+1(即將1所在的最高位後面的位的值全部換算爲0,然後對最高位後的所有位求和後再加1),tableSizeFor(int cap)方法的詳細運算過程如下:
static final int tableSizeFor(int cap) {//cap=137
第一步:int n = cap - 1;//n=137-1=136=0+0+0+8+0+0+0+128=0×2^0+ 0×21+0×22+ 1×2^3+ 0×2^4+ 0×2^5+ 0×2^6+ 1×2^7
//136是正數,所以其原碼/反碼/補碼對應的二進制表示都是一樣的,且一個int等於4個字節等於32比特,所以其二進制完整表示等於00000000 00000000 00000000 10001000
第二步:n|=n>>>1;//即n=n|(n>>>1);即此時n的二進制值 或位運算 此時n的二進制值無符號右移1位後的值

此時n的二進制值							00000000 00000000 00000000 10001000
  										或運算
此時n的二進制值無符號右移1位後的值			00000000 00000000 00000000 01000100
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11000100  

結論:主要是將爲1的最高位第8位向右移了1位,然後再與最高位爲1的原來的值進行或的位運算時,就可以保證最高位第8位和第7位必然都是1(注:這裏第3位也是1,是因爲原來的值裏的第4位本來就是1,右移1位後第4位變成第3位所以也是1,並非該算法導致的必然結果.)

第三步:n|=n>>>2;//即n=n|(n>>>2);即此時n的二進制值 或位運算 此時n的二進制值無符號右移2位後的值

此時n的二進制值							00000000 00000000 00000000 11000100
  										或運算
此時n的二進制值無符號右移2位後的值			00000000 00000000 00000000 00110001
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11110101  

結論:主要是將前2個已經爲1的最高位向右移了2位,然後再與前2個2最高位是1的值進行或的位運算時,就可以保證前4位都是1
第四步:n|=n>>>4;//即n=n|(n>>>4);即此時n的二進制值 或位運算 此時n的二進制值無符號右移4位後的值

此時n的二進制值							00000000 00000000 00000000 11110101  
  										或運算
此時n的二進制值無符號右移4位後的值			00000000 00000000 00000000 00001111
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11111111  

結論:主要是將前4個已經爲1的最高位向右移了4位,然後再與前4個最高位是1的值進行或的位運算時,就可以保證前8位都是1
注意:此時已經達到除高位1外,其他位都換算成1的目的了,所以後續的步驟的處理邏輯應該只是保持該結果,而不應該再對該結果有所變更.
第五步:n|=n>>>8;//即n=n|(n>>>8);即此時n的二進制值 或位運算 此時n的二進制值無符號右移8位後的值

此時n的二進制值							00000000 00000000 00000000 11111111  
  										或運算
此時n的二進制值無符號右移8位後的值			00000000 00000000 00000000 00000000
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11111111  

結論:由於最高位是第八位,且此時最高位後續的位的0都已經換算成了1,即前8位都是1了,即使再無符號右移了8位,然後再與前8位已經是1的值進行或的位運算,結果還是不變的.
第六步:n|=n>>>16;//即n=n|(n>>>16);即此時n的二進制值 或位運算 此時n的二進制值無符號右移16位後的值

此時n的二進制值							00000000 00000000 00000000 11111111  
  										或運算
此時n的二進制值無符號右移8位後的值			00000000 00000000 00000000 00000000
-----------------------------------------------------------------------------------------------
                                        00000000 00000000 00000000 11111111  

結論:同第五步的結論,由於最高位是第八位,且此時最高位後續的位的0都已經換算成了1,即前8位都是1了,即使再無符號右移了16位,然後再與前8位已經是1的值進行或的位運算,結果還是不變的.
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章