HashMap的hash算法相关连环炮

1. HashMap为什么异或原数右移16位计算哈希值?

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

简单的说如果key为null,返回0。否则返回key的hash值异或一个key的hash值右移16位。

我们看一下效果

0000 1010 1000 1000 1010 0011 0111 0100 `原数`

0000 0000 0000 0000 0000 1010 1000 1000 `右移16`

0000 1010 1000 1000 1010 1001 1111 1100 `异或结果`

我们发现,高位16没有发生变化,因为右移16位之后高位都是补0,1异或0还是1,0异或0还是0。

到此我们不能明确的知道,这个异或右移16位有什么作用,我们看一下HashMap如何计算插入位置的。

源码
if ((p = tab[i = (n - 1) & hash]) = = null)
            tab[i] = newNode(hash, key, value, null);

n为容量大小,假设我们现在的容量是起始容量16,则这里的算式就是15&hash值

我们看一下效果

1101 0011 0010 1110 0110 0100 0010 1011 `原数`

0000 0000 0000 0000 0000 0000 0000 1111 `15的二进制`

0000 0000 0000 0000 0000 0000 0000 1011 `结果`

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

也许你可能会说,即使丢失了高区特征不同hashcode也可以计算出不同的槽位来,但是你设想如果两个哈希值的低位差异极小而高位差异很大,导致这两个哈希值计算出来的桶位比较接近,会插入到HashMap的两个位置比较相邻的位置,这样哈希碰撞的概率就变高了!

我们认为一个健壮的哈希算法应该在hash比较接近的时候,计算出来的结果应该也要天差地别,足够的散列,所以这这个高位右移16位的异或运算也是HashMap将性能做到极致的一种体现。

2. HashMap的hash算法为什么使用异或?

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

3.可以用%取余运算吗?

&运算时二进制逻辑运算符,是计算机能直接执行的操作符,而%是Java处理整形浮点型所定义的操作符,底层也是这些逻辑运算符的实现,效率的差别可想而知,效率相差大概10倍。

发表评论

后才能评论