首先咱们先来了解一下如何使用key值的hashCode去寻址:
获取key值的hashCode 利用hashCode%数组长度得到index下标 这就是一个寻址的过程,但是取模是一个非常消耗性能的操作,并且如果张三和李四取模后得到下标标一致就会出现hash冲突,解决hash冲突又是是一个耗时的操作,所以JDK做了以下几个步骤来优化;
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这段代码是HashMap中在获取hashCode之后进行优化的方法,int是4个字节也就是32位,代码中看到key值的hashCode赋值给h,然后将h和h右移16位的结果进行了异或运算,什么意思呢,假设h原先的值为1111 1111 1111 1111 1011 1011 1111 0011(这是二进制的值,不懂的好好复习一下数据结构),右移16位之后呢得到了0000 0000 0000 0000 1111 1111 1111 1111,然后将两者进行亦或运算得到了1111 1111 1111 1111 0100 0100 0000 1100,这个得到的二进制结果他的第16位是不是同时包含了原先key值的hashCode的高16位和低16位的特征,先记住这一点为什么要这么做;
接下来我们说取模是一个非常耗时的操作,所以JDK选择了另外一种方式,也就是将异或得到的hashCode和数组长度进行&的操作,经过前人的努力,得到了一个公式,hashCode&(array.length-1)=hashCode%数组长度(这里有一个要求就是数组长度得是2的n次方),一般来说数组的长度初始是16,所以array.length-1也就是0000 0000 0000 0000 0000 0000 0000 1111(后称A),拿A和未经过处理的h去进行&运算是不是高16位根本都没有参与,因为A的高16位全部是0,&运算是其中一方为0结果就是0,所以如果只有h的低16位参与了运算是不是就加大了A&h结果相同的概率,所以JDK要将h右移16位在进行亦或运算,把高16位的特征也加入到低16位中去,这样A&h的结果相同的概率就小了,也就会减少处理hash冲突所带来的性能损耗;
|