为什么HashMap的长度是2的整数次幂?

先说答案:

为了加快哈希计算以及减少哈希冲突

为什么可以加快计算?

我们都知道为了找到 KEY 的位置在哈希表的哪个槽里面,需要计算 hash(KEY) % 数组长度

但是!% 计算比 & 慢很多

所以用 & 代替 %,为了保证 & 的计算结果等于 % 的结果需要把 length 减 1

也就是 hash(KEY) & (length - 1)

这个 hash(KEY) 没什么可说的,调用 Object 里面的 native 方法完成计算,一般返回的是一个整数,至于是偶数还是奇数就不一定了

我做了一个小实验:

假设现在数组的长度:2 ^ 14 = 16384

String key = "zZ1!." 的 hash 值为 115398910

public static void main(String[] args) {
        String key = "zZ1!.";
        System.out.println(key.hashCode());// 115398910
}

hash & (length - 1) = 115398910 & 16383 = 6398 (你可以使用电脑的计算机验证一下是不是对的)6398 的二进制是 ‭0001100011111110‬

hash % length = 115398910 % 16384 = 6398

结果确实一样,但是 & 运算更快!

还有一个有意思的事就是:因为扩容为 2 的倍数,根据 hash 桶的计算方法,元素哈希值不变而通过 % 计算的方式会因为 length 的变化导致计算出来的 hash 桶的位置不断变化。数据一致在漂移,影响性能!!

为什么可以减少冲突?

假设现在数组的长度 length 可能是偶数也可能是奇数

length 为偶数时,length-1 为奇数,奇数的二进制最后一位是 1,这样便保证了 hash &(length-1) 的最后一位可能为 0,也可能为 1(这取决于 h 的值),即 & 运算后的结果可能为偶数,也可能为奇数,这样便可以保证散列的均匀性

例如 length = 4,length - 1 = 3, 3 的 二进制是 11
若此时的 hash 是 2,也就是 10,那么 10 & 11 = 10(偶数位置)
hash = 3,即 11 & 11 = 11 (奇数位置)

而如果 length 为奇数的话,很明显 length-1 为偶数,它的最后一位是 0,这样 hash & (length-1) 的最后一位肯定为 0,即只能为偶数,这样任何 hash 值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间

length = 3, 3 - 1 = 2,他的二进制是 10
10 无论与什么树进行 & 运算,结果都是偶数

因此,length 取 2 的整数次幂,是为了使不同 hash 值发生碰撞的概率较小,这样就能使元素在哈希表中均匀地散列

补充:

(1)你补充的很对,这确实是源码中hash函数的做法。不过你说的是解释了源码右移背后的意义,没说为什么选择2^n, 其实你就只差一步了: 正是因为hash值就是要用低位的信息,那么结合&操作,&的另一个数最好低位全是1,这样&才有意义;否则结果就肯定是0那么&就没有意义,所以需要2^n

(2) 我觉得你说的有点片面

选取2的幂。减一后做与操作刚好截取hash值的低位。以16为例子,二进制是00....001111,做与操作截取的就是hash的低四位

为什么能减少碰撞?这是因为hashmap的hash值是hashCode右移16位得到的,这么做使得hash值的低位保留了高位的信息,所以只要低位就可以了。

如果你不右移,那么高位信息是被浪费的,只利用低位信息,哈希碰撞的几率会大大增加

因为用hash计算索引时用了扰动函数

转自:为什么HashMap的长度是2的整数次幂? - 知乎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值