关于HashMap中的位运算
为什么HashMap的数组大小一定是2的倍数?
主要有两点
1、当n为2的整数次幂,hash%n等价(n-1)&hash
这个公式怎么来的呢?
举个10进制的例子!
115%10 = 5;
由于10是十进制的整数次幂
1 1 5
1 0
——————
5 (将第二个1之前数都可以整除10(因为10是10的整数幂),所以只剩下5)
100110
00100
——————
10 (同样第三个1之前的数可以整除100(因为100【2进制】是2的整数幂)。所以只剩下10)
这时我们怎么可以得到1后面的数呢?
那就是&运算
100110
000011
————
10 (这与上面的公式是等价的,这就是hash%n等价(n-1)&hash由来)
第二个好处就是扩容时,节点可能需要重新分到不同的桶值时避免了重新计算桶值。
也就是计算(n-1)&hash
为什么呢?
前面说到容量必须2的整数次幂,通过看源码我们知道扩容一次增加2倍,也就是n<<1
100110
000100
————
10 没扩容前
100110
001000
————
10 扩容后
斜体样式扩容后的取余结果比原来的结果多往前加了一位
100110
原来: 10
扩容:110
(原来第三位如果是0)扩容前后余数不变,(原来第三位是1)扩容后的余数等于扩容前的余数+扩容前容量
我看了很多博客都说是容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少hash碰撞。
我的理解是
只要计算桶值公式为hash%n,无论n取什么值,它都是均匀分布的。
只是说当n为2的整数次幂时hash%n等价(n-1)&hash这才使的它可以均匀分布。
再啰嗦一点:为什么hashMap.hash()方法需要高低位异或。
static final int hash(Object key) {
int h;
//map的key可以为null因为hash值为0
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
上文说到取余公式(n-1)&hash的由来。余数就等于截取原来hash的后几位。
假设们不高低位异或。可能会出现某些key之间的hash值,只有高位不同,那么我们所求的余数就会是相同的。