重学Java基础之关键难点备忘-HashMap

目录

HashMap中hash(Object key)原理,为什么(hashcode >>> 16)

必备知识点.:& 运算、  |运算、  ^运算

按位与运算符(&)

按位或运算符(|)

异或运算符(^)

h >>> 16 是什么,有什么用?

为什么 h = key.hashCode() 与 (h >>> 16) 异或?

HashMap中根据hash值计算Entry[]中index的方法

为什么不用取模,要用位运算?

如何计算index?

为什么长度一定是2的n次方?


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,比如:

十进制二进制
1500001111
31

00011111

6300111111
12701111111
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值