目录
HashMap中hash(Object key)原理,为什么(hashcode >>> 16)
为什么 h = key.hashCode() 与 (h >>> 16) 异或?
HashMap中根据hash值计算Entry[]中index的方法
HashMap中hash(Object key)原理,为什么(hashcode >>> 16)
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
必备知识点.:& 运算、 |运算、 ^运算
按位与运算符(&)
运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为0
按位或运算符(|)
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :参加运算的两个对象只要有一个为1,其值为1。
异或运算符(^)
参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;
伏笔:可以发现,& 和 | 都让结果更加趋近于0和1,只有^ 使得结果是均匀分布的。
h >>> 16 是什么,有什么用?
h是hashcode。h >>> 16是用来取出h的高16,(>>>是无符号右移)
为什么 h = key.hashCode() 与 (h >>> 16) 异或?
我们可以查看获取index的原理(下文有详细说明):
p = tab[i = (n - 1) & hash]
//index= (n - 1) & hash
由于绝大多数情况下length一般都小于2^16(65536),所以hash参与计算的多为低16位(因为高16&的全是0,结果全是0,高16位就没有参与计算),碰撞会很严重。
我们不难发现:
当length=16时 下标运算结果取决于哈希值的低四位
当length=32时 下标运算结果取决于哈希值的低五位
当length=2的N次方, 下标运算结果取决于哈希值的低N位
所以,java为了增加低16位的随机性,避免hash碰撞,因此将hash值的高16位与hash值进行异或(因为相比&和|,^的结果更加随机和平均),增大低16位的随机性。
不得不感叹,设计的秒啊。
看了知乎上其他一些不错的回答:扰动函数
HashMap中根据hash值计算Entry[]中index的方法
为什么不用取模,要用位运算?
一般而言,hash值为int,index需要映射到0 ~ length -1,最直观的使用取模运算,也就是:
index = hash值 % length ,这个时候index的值的范围就是 0 ~ length -1
但是,Java 官方没有采用这个办法,因为这种效率不是最高的。
如何计算index?
为了解决取模效率的问题,Java官方采用了位运算的方法。
index = hash值 & (length-1)
为什么长度一定是2的n次方?
这个时候,如果要index的值的范围也是 0 ~ length -1,需要一个前置条件:
length必须是2的n次幂。当length是2的n次方,为正整数时,有以下的公式成立:
当length是2的n次方时,length-1对应二进制,右边低位全是1,比如:
十进制 | 二进制 |
15 | 00001111 |
31 | 00011111 |
63 | 00111111 |
127 | 01111111 |