Java11 HashMap中tableSizeFor(int)的實現

背景:由於HashMap提供了配置初始化容量的構造函數,爲了確保容量始終爲2的N次方,需要計算>=調用方傳入容量的最小的2的整數次方,我們記作n。函數tableSizeFor(int)實現了這一功能:

static final int tableSizeFor(int cap) {
    int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
    return n < 0 ? 1 : (n >= 1073741824 ? 1073741824 : n + 1);
}

解析:>>>爲無符號右移,-1的二進制表示爲111…111(32個1),Integer.numberOfLeadingZeros(int)計算輸入參數二進制表示的開頭0的個數,我們將其記作m。將-1無符號右移cap-1對應的m位之後,我們便可以得到n。Integer.numberOfLeadingZeros(int)的實現如下:

public static int numberOfLeadingZeros(int i) {
    if (i <= 0) {
        return i == 0 ? 32 : 0;
    } else {
        int n = 31;
        if (i >= 65536) {
            n -= 16;
            i >>>= 16;
        }

        if (i >= 256) {
            n -= 8;
            i >>>= 8;
        }

        if (i >= 16) {
            n -= 4;
            i >>>= 4;
        }

        if (i >= 4) {
            n -= 2;
            i >>>= 2;
        }

        return n - (i >>> 1);
    }
}

這裏的計算利用到了二分法,並且將分支判斷進行了合併使得代碼更加簡潔,我們通過幾個例子來更好地理解此函數的實現

  1. n = 0時,顯然,返回32

  2. n < 0時,由於負數的符號位爲1,所以返回0

  3. n > 0時,首先返回值在[1, 31]之間,n的初始值爲31,我們考慮66666的計算情況。首先66666的二進制表示爲0000 0000 0000 0001 0000 0100 0110 1010,那麼計算結果應該爲15。

    Step1: 判斷32-17位是否存在1。66666 >= 65536,說明66666的32-17位存在1,我們應該將16-1位去掉,將32-17位無符號右移到16-1位,即i >>>=16,相應地n -=16,此時i = 1,n = 15

    Step2: ‬判斷16-9位是否存在1。1 < 256,1的16-9位不存在1,全爲0,那麼說明66666的二進制表示中左邊至少存在8個連續的0,此時i = 1,n = 15

    Step3:判斷8-5位是否存在1。1 < 16,1的8-5位不存在1,全爲0,那麼說明66666的二進制表示中左邊至少存在8+4=12個連續的0,此時i = 1,n = 15

    Step4:判斷4-3位是否存在1。1 < 4,1的4-3位不存在1,全爲0,那麼說明66666的二進制表示中左邊至少存在12+2=14個連續的0,此時i = 1,n = 15

    Step5:判斷第二位是否爲1。1 < 2,1的第二位不是1,那麼說明66666的二進制表示中左邊至少存在14+1=15個連續的0

發佈了11 篇原創文章 · 獲贊 7 · 訪問量 1914
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章