我们看HashMap的源码可以知道,HashMap的长度强制为2的n次方
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) {
// HD, Count leading 0's
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);
}
那为什么HashMap的长度L需要是2的N次方呢?
往HashMap中存储一对k-v,那么要先计算key的hashcode,然后我们通过hashcode计算要把这个k-v存储到长度为L的数组的哪个位置
最简单的,我们可以通过模运算 hashcode%L 来计算存储的位置,但是模运算的效率相对较低,HashMap使用位运算(与运算)来计算存储的位置:hashcode&(L-1),这比模运算要快很多
比如长度L为16,hashcode是 ……1001,那么hashcode&(L-1)为1001&1111=1001=9,其实就是根据hashcode的最后四位来决定放到哪个桶里
如果长度L不为2的N次方,会怎么样呢?
比如长度L为10,任何一个hashcode和1001做与运算,都不可能等于0110,这样就会有几个位置,永远不可能存储到数据
hashcode和一个二进制数做与运算,如果这个二进制数的某一位为0,那么这一位与运算的结果永远不可能为1,也就是说,如果这个二进制数不是每一位都是1,那么一定会有一些结果永远不可能达到。所以这个二进制数的每一位都要是1,换成十进制这个数必须是2的N次方减1。