HashMap对hash算法的优化

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        ...
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
        ...

这里提出一个公式:当n为2的指数幂,x%n 等同于x&(n - 1)
由于按位与操作比取模的性能要高,所以为了使用按位与来对hash值取模,HashMap通过tableSizeFor()方法保证了数组长度n为2的指数幂。

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

HashMap没有直接使用key的hashCode值,而是拿hashCode值与自身右移16位后的结果做异或操作。
原因是为了让key的hashCode的高16位参与取模。
hashCode右移16位后高16位全部为0:

1111 1111 1111 1111 1111 1111 0101 0101 //右移前
0000 0000 0000 0000 1111 1111 1111 1111 //右移后

而这俩个值的异或操作的结果高16位保持不变,低16位就是原来的低16位与高16位的异或结果。
再来看n-1的值,由于n是2的指数幂,所以不难推出n-1的二进制如下:

0000 0000 0000 0000 0000 0000 0000 0001 //1
0000 0000 0000 0000 0000 0000 0000 0011 //3
0000 0000 0000 0000 0000 0000 0000 0111 //7
0000 0000 0000 0000 0000 0000 0000 1111 //15
0000 0000 0000 0000 0000 0000 0001 1111 //31

显然,在数据量不多的情况下,n-1的高16位都是0,而与0做按位与结果还是0,相当于将hash值的高16位忽略掉了。
那么hash值若是存在高16位不同,而低16位完全相同的数时,就会因为n-1的特性导致对俩个完全不同的hash值取模结果却相同,产生大量的hash冲突。例如:

1111 0011 1010 1111 0101 0101 0101 0101  //hash值1
1100 1100 1010 1100 0101 0101 0101 0101  //hash值2

因此对hash算法的优化就是为了让hashCode的高低位都参与取模,降低冲突概率。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值