2的n次方 java 溢出,HashMap的长度为什么要是2的n次方

HashMap为了存取高效,要尽量较少碰撞,就是要尽量把数据分配均匀,每个链表长度大致相同,这个实现就在把数据存到哪个链表中的算法;

这个算法实际就是取模,hash%length,计算机中直接求余效率不如位移运算,源码中做了优化hash&(length-1),

hash%length==hash&(length-1)的前提是length是2的n次方;

为什么这样能均匀分布减少碰撞呢?2的n次方实际就是1后面n个0,2的n次方-1  实际就是n个1;

例如长度为9时候,3&(9-1)=0  2&(9-1)=0 ,都在0上,碰撞了;

例如长度为8时候,3&(8-1)=3  2&(8-1)=2 ,不同位置上,不碰撞;

其实就是按位“与”的时候,每一位都能  &1  ,也就是和1111……1111111进行与运算

0000 0011     3

& 0000 1000      8

= 0000 0000      0

0000 0010     2

& 0000 1000    8

= 0000 0000      0

-------------------------------------------------------------

0000 0011     3

& 0000 0111     7

= 0000 0011       3

0000 0010      2

& 0000 0111      7

= 0000 0010      2

当然如果不考虑效率直接求余即可(就不需要要求长度必须是2的n次方了)

JDK1.8 中 hash 函数的实现

JDK1.8中再次优化了这个哈希函数,把 key 的 hashCode 方法返回值右移16位,即丢弃低16位,高16位全为0 ,然后在于 hashCode 返回值做异或运算,即高 16 位与低 16 位进行异或运算,这么做可以在数组 table 的 length 比较小的时候,也能保证考虑到高低Bit都参与到 hash 的计算中,同时不会有太大的开销,扰动处理次数也从 4次位运算 + 5次异或运算 降低到 1次位运算 + 1次异或运算

static final int hash(Object key) {

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

复制代码

进过上述的扰动函数只是得到了合适的 hash 值,但是还没有确定在 Node[] 数组中的角标,在 JDK1.7中存在一个函数,JDK1.8中虽然没有但是只是把这步运算放到了 put 函数中。我们就看下这个函数实现:

static int indexFor(int h, int length) {

return h & (length-1); // 取模运算

}

复制代码

为了让 hash 值能够对应到现有数组中的位置,我们上篇文章讲到一个方法为 取模运算,即 hash % length,得到结果作为角标位置。但是 HashMap 就厉害了,连这一步取模运算的都优化了。我们需要知道一个计算机对于2进制的运算是要快于10进制的,取模算是10进制的运算了,而位与运算就要更高效一些了。

我们知道 HashMap 底层数组的长度总是 2^n ,转为二进制总是 1000 即1后边多个0的情况。此时一个数与 2^n 取模,等价于 一个数与 2^n - 1做位与运算。而 JDK 中就使用h & (length-1) 运算替代了对 length取模。我们根据图片来看一个具体的例子:

format,png

图片来自:https://tech.meituan.com/java-hashmap.html 侵删。

小结

通过上边的分析我们可以到如下结论:

在存储元素之前,HashMap 会对 key 的 hashCode 返回值做进一步扰动函数处理,1.7 中扰动函数使用了 4次位运算 + 5次异或运算,1.8 中降低到 1次位运算 + 1次异或运算

扰动处理后的 hash 与 哈希表数组length -1 做位与运算得到最终元素储存的哈希桶角标位置。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值