为什么HashMap数组的长度必须是2的指数次幂

分析&回答

先看源码如何计算Hash值,

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

计算机的运行效率:加法(减法)>乘法>除法>取模


数组一旦达到容量的阈值的时候就需要对数组进行扩容。那么扩容就意味着要进行数组的移动,数组一旦移动,每移动一次就要重回记算索引,这个过程中牵扯大量元素的迁移,就会大大影响效率。那么如果说我们直接使用与运算,这个效率是远远高于取模运算的。

// 看看putVal 方法使用与运算
if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);
    
tab[i = (n - 1) & hash]中tab就是HashMap的实体数组,
其下标通过i = (n - 1) & hash来获取(n表示数组长度,hash表示hashCode值),
1、与运算(n-1) & hash
2、取代取模运算hash%length
两种方式记算出来的结果是一致的(n就是length),也就是(length-1)&hash = hash%length

当数组的长=长度为2的指数次幂时,例如:假设数组长度为4,哈希值为10
(n-1) & hash = (4-1) & 10 = 00000011 & 00001010 = 00000010 = 2
hash % length = 10 % 4 = 2
即length-1)&hash = hash&length

但是当数组的长=长度不为2的指数次幂时,例如:再假设数组长度为5,哈希值10
(n-1) & hash = (5-1) & 10 = 00000100 & 00001010 = 00000000 = 0 
hash % length = 10 % 5 = 2
即length-1)&hash ≠ hash&length

通过上面看出,这必须保证数组长度为2的整数次幂。

综上所述使用位运算来加快计算的效率,只有当数组长度为2的指数次幂时,其计算得出的值才能和取模算法的值相等,并且保证能取到数组的每一位,减少哈希碰撞,不浪费大量的数组资源。

反思&扩展

为什么要无符号右移16位后做异或运算?

image.png

将h无符号右移16为相当于将高区16位移动到了低区的16位,再与原hashcode做异或运算,可以将高低位二进制特征混合起来

从上文可知高区的16位与原hashcode相比没有发生变化,低区的16位发生了变化

我们可知通过上面(h = key.hashCode()) ^ (h >>> 16)进行运算可以把高区与低区的二进制特征混合到低区,那么为什么要这么做呢?

我们都知道重新计算出的新哈希值在后面将会参与hashmap中数组槽位的计算,计算公式:(n - 1) & hash,假如这时数组槽位有16个,则槽位计算如下:

image.png

仔细观察上文不难发现,高区的16位很有可能会被数组槽位数的二进制码锁屏蔽,如果我们不做刚才移位异或运算,那么在计算槽位时将丢失高区特征

也许你可能会说,即使丢失了高区特征不同hashcode也可以计算出不同的槽位来,但是细想当两个哈希码很接近时,那么这高区的一点点差异就可能导致一次哈希碰撞,所以这也是将性能做到极致的一种体现。


喵呜面试助手: 一站式解决面试问题,你可以搜索微信小程序 [喵呜面试助手] 或关注 [喵呜刷题] -> 面试助手 免费刷题。如有好的面试知识或技巧期待您的共享!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喵呜刷题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值