细粒度拆分HashMap<2>

本文详细探讨了HashMap的hash函数计算,包括左移、右移和无符号右移的区别,以及为何在HashMap中使用无符号右移16位。通过实例分析了右移16位能有效减少哈希冲突的原因,同时提到了hashCode选择31作为乘数的性能考虑。文章适合面试准备和理解HashMap内部机制。
摘要由CSDN通过智能技术生成

对于上篇文章遗漏了一点:

在进行<<、>>和>>>运算的时候需要考虑正负的情况

<<:不分正负数,低位补0;

“>>”:如果该数为正,则高位补0,若为负数,则高位补1;

“>>>”:若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0

在面试HashMap的时候,你是不是背面试答案还是真的有去研究分析源码,面试官一旦深入一问便能一下子明白,可能不会问得那么深,问到hash算法的实现原理,但是为了能够吊打面试官,这个必须要掌握。

上期我们主要对HashMap的成员变量进行了讲解:

细粒度拆分HashMap<1>

面试场景:面试者太多了,胖虎刚好是最后一个面试者,面试官面了前面很多人感觉大家的答案都如出一辙,像极了背面经,有些失望,所以面试官打算:

一、面试官一上来就问:你知道hash函数的作用吗?

胖虎:(不对啊,你怎么不问我HashMap的数据结构啥的,前面的人都问了,而我一来就直接怼hash函数,还好我有做准备)HashMap就是通过计算key的hash值,通过此值并利用hashCode以及equals来进行寻址操作。

二、面试官: 好的,那你知道是怎么计算hash值的吗?

hash函数:

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

胖虎:(还好我有准备,这个简单)首先判断key==null哈希值就返回0,接着就是利用hashCode先计算hash值,然后将该值进行无符号右移16位得出另一个值,再将两者进行异或,就得出来了。

在这里插入图片描述

(面试官心里想:看来还真的有去分析源码。)

三、面试官:那如果出现hash碰撞要怎么办?

hash碰撞:不同的hashcode也可能生成相同h,这种冲突只能尽量避免,无法直接杜绝。

胖虎:(这个难不倒我,我有链地址法)
在这里插入图片描述
链地址法: 通过将哈希表每一个单元中设置链表,某个数据项对的关键字还是像通常一样映射到哈希表的单元中,而数据项本身插入到单元的链表中,意思就是来一个相同的数据,就将它插入到单元对应的链表中,在来一个相同的,继续给链表中插入。

胖虎:(心里想,应该不会再问了)

四、面试官:(心里想:既然这样我问个你不会的)你知道hash函数的计算过程,那你有没有思考过为什么要右移16位吗?
在这里插入图片描述

需要从getNode方法中的:first = tab[(n - 1) & hash]) != null开始分析:

假如有这两个对象的hashCode:
对象A:1000010001110001000001111000000
对象B: 0111011100111000101000010100000
如果数组长度是16,那么进行15的二进制是1111,与对象A和对象B进行&操作后结果都是0,这可不是我们想要的结果。

将hashCode右移16位,也就是取int类型的一半,刚好将二进制数对半切开,然后使用异或。(如果两个数对应的位置相反,则结果为1,反之为0),这样就能避免上面的情况发生。

在一些极端情况下还是有问题,比如:10000000000000000000000000 和 1000000000100000000000000
这两个数,如果数组长度是16,那么即使右移16位,再异或,hash值还是会重复。但是为了性能,对这种极端情况,JDK的作者选择了性能。毕竟这是少数情况,为了这种情况去增加 hash时间,性价比不高。

五、面试官:(这都难不住他,问个连我也不会的),既然你知道在计算hash值的时候要右移16位,那你知不道为什么大部分hashCode要选择使用31?
在这里插入图片描述
在这里插入图片描述
胖虎:(你TM存心刁难我吧,老子面试的是实习岗位,不带这么问的啊?人家3年的都不问,还好我早又准备:)
在这里插入图片描述

在名著 《Effective Java》第 42 页就有对 hashCode 为什么采用 31 做了说明:

之所以使用 31, 是因为他是一个奇素数。如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算(低位补0)。使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果。 31 有个很好的性能,即用移位和减法来代替乘法,可以得到更好的性能: 31 * i == (i << 5) - i。

在这里插入图片描述
面试官:牛皮,想不想加入我司的人才库,人才精英库哦?

在这里插入图片描述

在这里插入图片描述

未终。。。。

文段中涉及到了2的幂次方,下文将从容量的角度来讲解此类问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值