最近因为刚好被问到,HashMap中hash算法做了什么优化,没回答上来,所以去看了下源码,百度了学习了一下。 首先先看下1.8里面key是如何进行hash操作的
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
关于位异或^的,我在看源码的时候也写了一点点笔记,juejin.cn/user/278878… 我们可以看到,它不是直接拿key的hashCode的,而是做了一层运算,为什么要多这一层操作呢,这个涉及到定位table表的下标的计算了,在下面讲。 我们从这个公式可以看到,这里是对key的hashCode先进行了移位运算,将hashCode向右移动了16位,然后跟原来的hashCode进行了位异或运算。大致的流程如下
hashCode最初的值 1001 0000 0000 1000 0000 0000 0001 1111
进行移位运算后 0000 0000 0000 0000 1001 0000 0000 1000
执行异或运算后 1001 0000 0000 0000 1001 0000 0001 0111
我们这样运算下来,可以看到,因为将原hashcode的高位与低位进行了运算,所以使得低位的变化更加丰富了(这个是重点) 接下来就要看源码中对于key在table下标的定位逻辑了
就是我框中标出来的那部分,这个实际上还是我们的开放寻址法,是由hash%n对n取余的变形。
前提条件是n是2的幂次方,也就是n=2^x,那么就有(n-1)&hash=hash%n,这里用位运算是因为位运算相对于取余运算要高效很多。
然后我们看下n-1,一般情况下,n的值不会大到哪里取,所以对应的值如下:
0000 0000 0000 0000 0000 0000 0000 0001 //n=2
0000 0000 0000 0000 0000 0000 0000 0011 //n=4
0000 0000 0000 0000 0000 0000 0000 0111 //n=12
0000 0000 0000 0000 0000 0000 0000 1111 //n=16
我们可以观察到,n的值主要是在低位体现,这时候联系到前面对hash的操作,目的是用高位异或来使得hash值得低位变得更丰富,这样在对n-1进行位与运算的时候,得出的值会更加丰富,起到更加有效避免hash冲突的效果。
注意:1.7对hash值的操作实际上是对hashCode进行了4次位运算,1.8变成只有一次,网上说这样是更加效率,质量来考虑。具体的也不太清楚,目前只能简单的理解为,进行一次位运算的时间比较少了。