背景:由於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);
}
}
這裏的計算利用到了二分法,並且將分支判斷進行了合併使得代碼更加簡潔,我們通過幾個例子來更好地理解此函數的實現
-
n = 0時,顯然,返回32
-
n < 0時,由於負數的符號位爲1,所以返回0
-
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