HashMap里面的散列函数是什么

HashMap里面的散列函数(求在数组中的下标)是什么

(length - 1) & hash 首先要知道一点,HashMap的容量大小一定是2的幂次形式(1,2,4,8…). 原因的话可以看一下上一篇文章HashMap源码解析(一) 当执行new HashMap时都执行了什么?. 大致就是第一次初始化容量的时候hashmap会初始化为比传入容量大小大的最小的二的次幂,后面扩容的时候每次乘二(实际用的是左移操作).
知道了这一点之后我们就知道length是二的次幂. 那么对于一个二的次幂减一之后的二进制表示是什么样的呢

2-1 -> 1
4-1 -> 11
8-1 -> 111
16-1 -> 1111

看出规律了吧.就是把length的二进制表示去掉了最高位然后把所有的低位都补了1
然后看一下&也就是 与 操作.每一位&0之后都会是0.如果了解计算机网络的话,应该就很容易看出这个(length-1)&hash的作用了.(length-1)实际上的作用是掩码,把所有的高位全部过滤掉.得到的余下的数一定小于length. 可以直接作为在table数组的下表了. 到这里看来,这个过滤操作本身就可以作为一个散列函数了.
但是我们最常用的散列函数不是取余数吗?其实这个操作本身得到的结果和hash%length取余的结果是一样的.我们看一下余数的定义:余数指整数除法中被除数未被除尽部分
再来看一下我们的操作我们把高位都过滤掉了,那些高位代表的数一定是length的倍数,那么余下来的小于length的数不就是余数了.所以HashMap中使用了一个位运算来巧妙且快速的求出了余数.

HashMap中的Hash函数(求hash值)

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

最近阅读HashMap源码的时候看到HashMap里的hashcode对Key本身的hashcode进行了额外的操作key.hashCode()) ^ (h >>> 16);
查了一些资料看了一下这里为什么要进行这样的操作:

这个操作实际上是进行了一次扰动,可以理解为对原来的hash码又进行了一次随机化的操作来减少碰撞.

先看一下这个操作做了什么,从源码可以看出来将原来hash码的低十六位和自己的高十六位进行了一次异或操作.也就是为低十六位进行了又一次的随机操作.根据上面的介绍我们很容易想到当我们的数据量不是足够大时,基本上用到的只有hash码的后面几位,前面的都被掩码掩盖掉了(取余取掉了).
这样做的好处是可以减少碰撞,但是具体怎么减少的就不得而知了,可以参考一下扰动函数. 文章里举了个例子:
随机选取了352个字符串,在他们散列值完全没有冲突的前提下,对它们做低位掩码,取数组下标。在这里插入图片描述
做了扰动操作的碰撞数有所改善,碰撞减少了10%左右.也算是很大的提高了.
顺带提一句,Jdk7中做了四次扰动操作,JDK8只做了一次.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值