HashMap中的hash算法中的几个疑问

HashMap中哈希算法的关键代码

//从新计算哈希值
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//key若是是null 新hashcode是0 不然 计算新的hashcode
}
//计算数组槽位
 (n - 1) & hash

HashMap的细节咱们不谈,只看这个哈希算法的细节(h = key.hashCode()) ^ (h >>> 16)算法

^按位异或运算,只要位不一样结果为1,否则结果为0;
>>> 无符号右移:右边补0数组

为何要无符号右移16位后作异或运算

根据上面的说明咱们作一个简单演练性能

将h无符号右移16为至关于将高区16位移动到了低区的16位,再与原hashcode作异或运算,能够将高低位二进制特征混合起来spa

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

咱们可知经过上面(h = key.hashCode()) ^ (h >>> 16)进行运算能够把高区与低区的二进制特征混合到低区,那么为何要这么作呢?blog

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

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

也许你可能会说,即便丢失了高区特征不一样hashcode也能够计算出不一样的槽位来,可是细想当两个哈希码很接近时,那么这高区的一点点差别就可能致使一次哈希碰撞,因此这也是将性能作到极致的一种体现效率

使用异或运算的缘由

 异或运算能更好的保留各部分的特征,若是采用&运算计算出来的值会向1靠拢,采用|运算计算出来的值会向0靠拢hashmap

为何槽位数必须使用2^n

一、为了让哈希后的结果更加均匀

这个缘由咱们继续用上面的例子来讲明

假如槽位数不是16,而是17,则槽位计算公式变成:(17 - 1) & hash

从上文能够看出,计算结果将会大大趋同,hashcode参加&运算后被更多位的0屏蔽,计算结果只剩下两种0和16,这对于hashmap来讲是一种灾难

二、能够经过位运算e.hash & (newCap - 1)来计算,a % (2^n) 等价于 a & (2^n - 1)  ,位运算的运算效率高于算术运算,缘由是算术运算仍是会被转化为位运算

 

 

 

说了这么多点,上面提到的全部问题,最终目的仍是为了让哈希后的结果更均匀的分部,减小哈希碰撞,提高hashmap的运行效率

相关文章
相关标签/搜索