我们都知道 hashmap 的底层是一个数组加链表的结构,当向其中添加一个元素的时候,需要根据key的hash值,去确定其在数组中的具体位置。
看源码,我们可以发现,确定数组位置的实现是 i=(n-1)& hash
,其中 n 代表数组的长度,即map的容量。
当n为2的幂次方时,(n-1)& hash 的值是均匀分布的,我们假设n=16,hash从0开始递增:
hash | (n-1)& hash | 结果 |
---|---|---|
0 | 1111 & 0 | 0 |
1 | 1111 & 1 | 1 |
2 | 1111 & 10 | 2 |
3 | 1111 & 11 | 3 |
4 | 1111 & 100 | 4 |
5 | 1111 & 101 | 5 |
…… | …… | …… |
16 | 1111 & 10000 | 0 |
17 | 1111 & 10001 | 1 |
18 | 1111 & 10010 | 2 |
当n不为2的幂次方时,(n-1)& hash 的值不是是均匀分布的,我们假设n=15,hash从0开始递增:
hash | (n-1)& hash | 结果 |
---|---|---|
0 | 1110 & 0 | 0 |
1 | 1110 & 1 | 0 |
2 | 1110 & 10 | 2 |
3 | 1110 & 11 | 2 |
4 | 1110 & 100 | 4 |
5 | 1110 & 101 | 4 |
…… | …… | …… |
16 | 1110 & 10000 | 0 |
17 | 1110 & 10001 | 0 |
18 | 1110 & 10010 | 2 |
由上面可以看出,当我们根据key的hash确定其在数组的位置时,如果n为2的幂次方,可以保证数据的均匀插入,如果n不是2的幂次方,可能数组的一些位置永远不会插入数据,浪费数组的空间,加大hash冲突。
另一方面,一般我们可能会想通过 % 求余来确定位置,这样也可以,只不过性能不如 & 运算。而且当n是2的幂次方时:hash & (length - 1) == hash % length
因此,HashMap 容量为2次幂的原因,就是为了数据的的均匀分布,减少hash冲突,毕竟hash冲突越大,代表数组中一个链的长度越大,这样的话会降低hashmap的性能。