【java】关于HahMap中JDK1.7版本和JDK1.8版本 hash()方法

关于JDK1.7和JDK1.8 hash()方法

前言

HashMap中hash方法返回的值用于哈希散列得到一个下标,JDK1,7和JDK1.8,hash()方法的原理几乎相同,不过还是有不同的地方听我一一道来。

JDK1.7 hash()方法

 final int hash(Object k) {
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }
        h ^= k.hashCode();
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
  }

static int indexFor(int h, int length) {
        return h & (length-1);
}

首先HashMap是一张哈希表,储存的是键值对,在存储时,会计算出键的Hashcode,对该hashcode进行一些变换后,使用该hashcode对HashMap的容量减一取余,计算出下标。

indexFor方法就是计算下标使用的,只不过是采用位运算(&)的方式,这里的位运算其实就是取余(%)的意思
比如
HashMap的容量为16,hash方法的返回值是10101010110
16 - 1 = 15 = 01111
10101010110 & 01111 = 0110 = 6
10101010110 & 01111 这个运算可以理解成

使用位运算的好处

  1. 从速度上来说,位运算的预算效率要远远快于取余运算
  2. hashcode的大小是一个4字节的数,也可以理解成一个int类型的数据,也就是说它的取值范围是-2147483648~2147483647,也就说有负数的存在,如果我们不采用位运算,是不是通过取余运算取出一个负的值,对于数组来说负数的下标肯定是不存在的,如果使用位运算就能解决掉负数的问题。大家看一下下面的例子
  3. 在这里插入图片描述
    运行结果:
    在这里插入图片描述
    从运行结果来看,当我们的hashcode为负数进行按位与运算时,得到的结果依然是大于0的

位运算为什么可以代替取模运算

任何数对16的取模的运算结果一定是一个0~15(包括0和15)的数和取出一个数的低4位(即与15按位相与)的结果基本一样。
比如
17 = 10001(B)
15 = 01111(B)
17 & 15 = 10001 & 01111 = 001 = 1(D)
为了么说结果基本一样呢
10 = 1010(B)
7 = 0111(B)
10 & 7 = 1010 & 0111 = 010 = 2(D)
而10 % 7 = 3
我们进行位运算的目的是哈希散列得到下标,并不是得到一个准确的取余结果,这种不一样是可以接受的。

关于hash()返回部分的讲解

h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
我相信大家看到这部分代码时,内心时懵逼的,为什么不能直接用键得hashcode呢,非要进行这么多次对hashcode的变化。其实这个一种减少哈希冲突的一种手段。
比如
hashMap的容量为8,(也就是对8 - 1进行按位相与)7的二进制为0111
1101 0101 & 0111 = 101
0101 0101 & 0111 = 101
我们会发现就算hsahcode不一样,但是进行位运算的结果是一样的,因为他们的后三位都相等,这样就会形成哈希冲突
所以
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
目的是对hashcode进行扰乱操作,降低hashcode不同但是位运算结果相同的可能性,降低了哈希冲突

JDK1.8 hash()方法

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

在JDK1.8的实现中,原理上没有太大的变化,可以认为是JDK1.7版本的优化版本,hash方法的返回值通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的。以上方法得到的int的hash值,然后再通过h & (table.length -1)来得到该对象在数据中保存的位置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值