基于JDK11
我们都知道,在声明一个HashMap的时候能够指定the initial capacity(初始容量),如果我们指定了一个初始容量cap,那么就需要根据cap计算出相应的threshold(下次扩容的大小)
Talk is cheep
static final int tableSizeFor(int cap
) {
int n
= -1 >>> Integer
.numberOfLeadingZeros(cap
- 1);
return (n
< 0) ? 1 : (n
>= MAXIMUM_CAPACITY
) ? MAXIMUM_CAPACITY
: n
+ 1;
}
@HotSpotIntrinsicCandidate
public static int numberOfLeadingZeros(int i
) {
if (i
<= 0)
return i
== 0 ? 32 : 0;
int n
= 31;
if (i
>= 1 << 16) { n
-= 16; i
>>>= 16; }
if (i
>= 1 << 8) { n
-= 8; i
>>>= 8; }
if (i
>= 1 << 4) { n
-= 4; i
>>>= 4; }
if (i
>= 1 << 2) { n
-= 2; i
>>>= 2; }
return n
- (i
>>> 1);
}
移位分析
首先numberOfLeadingZeros计算的是一个int值使用二进制表示时,从左往右连续为0的个数。整个方法的思路是将输入的数不断的进行有条件的右移,右移的过程中将不可能存在的0的个数减去,之所以可能位移16, 8, 4, 2, 1是因为经过这些位移可以将所有int值都进行一次完整的位移(最后直剩下1或0)
public static void main(String
[] args
) {
System
.out
.println(Integer
.numberOfLeadingZeros(2));
System
.out
.println(Integer
.numberOfLeadingZeros(4));
}
tableSizeFor方法中,int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);的操作其实是先计算出cap-1左侧的0的个数i,然后将-1右移动i位,>>>表示使用0填充移位后的空位,>>运算符会使用符号位的值填充移位后的空位.
计算过程
假如cap = 16cap - 1 = 15 // 0000 0000 0000 0000 0000 0000 0000 1111Integer.numberOfLeadingZeros(cap - 1) = 28-1 // 计算机中的二进制表示:1111 1111 1111 1111 1111 1111 1111 1111(补码)-1 >>> 28 // 0000 0000 0000 0000 0000 0000 1111n = 15最后取n + 1作为桶(数组)的数量,这样是为了在计算Hash值的时候同样可以使用位操作减少冲突,保持数据的分散
我的个人博客有空来坐坐
https://www.wangyanan.online