【面试题之】HashMap的hash方法

手撕HashMap里面需要注意的就是hash方法的实现,一般来说,网上的hash方法的实现,主要有两种:

还有一种是下面的:

其实这两种都对,只是他们是不同java版本里面的,上面的是java7里面的,下面是java8里面的。这里面的代码内容实际上就是一个“扰动函数”,之所以做这个改动是因为作者觉得Java 8里面扰动做一次就够了,做4次的话,多了可能边际效用也不大,所以为了效率考虑就改成一次了。

第一个函数里面的参数h实际上就是key 的hashcode(),返回一个int型散列值,而如果直接拿散列值作为下下标来访问数组的话,因为int是32位的数据类型,一共包含的映射空间有超过40亿,但问题是,40亿长度的数组,内存是放不下的,而且hashmap 的初始长度才16,所以这个散列值不能直接拿来用,源码里面使用了一个indexFor函数,这个函数实际上就是把散列值和数组长度-1做了一个“与操作”,这个操作的本质实际上就是一个“低位掩码”,也就是说“与”操作的结果是散列值的高位全部归零,我们只会保留低位的值,比如如果数组长度为16,那么“与”上15的二进制就是1111,实际上就是只保留散列值的最低四位。

但是这个时候就会有问题,因为即使散列值分布的再松散,你取的只是他的低位,这样的话出现哈希冲突的概率也会很大。就比如如果哈希函数取的不好,刚好散列值是一个等差数列,因为只取低位,不取高位,所以就会导致最后有很多哈希冲突的。这个时候就需要一个扰动函数了,比如上面java8的扰动函数。他是把原本的hashcode值右移(无符号右移>>>)16位然后和原本的进行异或操作,再用这个结果放入indexFor计算下标,如下图:

 这里右移16位,就是32位的一半,实际上就是自己的高位和低位进行异或,实际上就是把原本的hashcode的高位和低位进行混合,增大随机性,同时也可以在低位中掺杂高位的信息,这样高位的信息也被变相保存下来了。即增加这个“扰动函数”,可以减小发生哈希冲突的概率。

参考:

https://www.zhihu.com/question/20733617

猜你喜欢

转载自blog.csdn.net/qq_35590091/article/details/107491416