HashMap 中数组的 size 为什么必须是 2 的整数次幂?(index如何确定和高16bit ^ 低16bit)

一、HashMap put操作的时候,index下标如何确定

HashMap<String,Integer> map = new HashMap<>();
map.put(“book”,1);

HashMap为k-v模型,put的过程中,计算下标时,分为两步,首先对 key 进行 hash 操作,其次再通过hash值和数组长度-1进行&运算得到下标,

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
int index = (array.length - 1) & hash; //array为哈希表的长度
  • hash函数是先拿到key 的hashcode,是32位的int值,再让hashcode的高16位和低16位进行异或操作
    可以看到这个函数大概的作用就是:高16bit不变,低16bit和高16bit做了一个异或。

  • 而计算下标index的时候,是使用&位操作,而非%求余。

二、为什么要 高16位 ^ 低16位

1. 数组空间无法满足
因为 key.hashCode() 函数调用的是key键值类型自带的哈希函数,返回int型散列值。int值范围为-2147483648~2147483647,前后加起来大概40亿的映射空间。只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个40亿长度的数组,内存是放不下的。

2.降低hash碰撞

  • 这么做可以在数组array的长度比较小的时候,也能保证考虑到高低bit都参与到hash的计算中;右位移16位,正好是32bit的一半,自己的高半区和低半区做异或,就是为了混合原始哈希码的高位和低位,以此来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。

那么为什么不用 &、| 运算呢? 因为&运算结果偏向于0,而 | 运算结果偏向于1;而 ^ 运算结果为 1 和 0 的概率一致

在这里插入图片描述

3.提升速度

算法一定要尽可能高效,因为这是高频操作, 因此采用位运算

三、为什么数组的长度是2^n

  • 因为可以有效的降低下标index值的冲突,从而满足哈希算法均匀分布的原则

举个例子:以“book”为Key来演示整个过程:

1.计算book的hashcode,结果为十进制的3029737,二进制为0000 0000 0000 0010 1110 0011 1010 1110 1001

3hash =(hashcode>>>16) ^ hashcode, 结果为:1110 0011 1010 1110 1011

2.假定HashMap长度是默认的16,计算Length-1的结果为十进制的15,二进制为1111。

3.把以上两个结果做与运算,1110 0011 1010 1110 1011 & 0000 0000 0000 1111 = 1011,十进制是11,所以 index=11

可以说,Hash算法最终得到的index结果,完全取决于Key的Hashcode值的最后几位。

假设HashMap的长度是10,重复刚才的运算步骤:

hash: 1110 0011 1010 1110 1011
length-1: 	     		  1001
index: 			          1001 = 9

单独看一个好像没有问题,再来试一个新的值

hash: 1110 0011 1010 1110 1001
length-1:	         	  1001
index:		              1001 = 9 	
hash: 1110 0011 1010 1110 1111
length-1:	         	  1001
index:		              1001 = 9 

通过例子发现,虽然Hash的倒数第二第三位都有改变,但是运算的index结果都是1001。也就是说,当HashMap长度为10的时候,有些index结果的出现几率会更大,而有些index结果永远不会出现(比如0111)

这样,显然不符合Hash算法均匀分布的原则

反观长度16或者其他2的幂,因为这样(数组长度-1)正好相当于一个“低位掩码”。与操作的结果就是散列值的高位全部归零只保留低位值,用来做数组下标访问。这也保证了index的值始终是在[0,array.leng)
Length-1的值是所有二进制位全为1,这种情况下,index的结果在&运算后就大致的可以看做等同于Hash后几位的值。只要输入的Hash本身分布均匀,Hash算法的结果就是均匀的。


  1110 0011 1010 1110 1011
& 0000 0000 0000 0000 1111
----------------------------------
  0000 0000 0000 0000 0101    //高位全部归零,只保留末四位
  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值