HashMap中的put方法中的hash方法
以下是put方法的代码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
当我第一次看到这个地方的时候,首先好奇的是这个hash
方法到底干了什么,于是我点了进去,看到下边这样的东西,顿时懵了:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
我懵的原因是因为我看到了^
、16
,这俩东西。到底是什么原因要做这样的操作呢?
在此之前,我们需要了解hashCode
是干嘛的,在上一篇文章里,我有写过关于hashCode在HashMap中充当的作用
,想了解的可以看这篇[HashMap源码学习之路]—hashcode的作用及数组长度为什么是2的n次幂
首先,说一下这个数字16
,对于我们得到的hashCode
值,它是int
类型,而int
类型,最大数转换成二进制的长度是32
位,对于它的一半是16
,这个概念,没毛病吧?
那上边的h>>>16
换句话说,就是将int类型右移16
位,那对于这样的操作,我们会得到这个int
类型数的一个高16
位的数。举一个小的例子,比如右移2
位,以数字11为例,转换成2进制是1011
,它右移2
位,就是0010
,具体如下:
11[十进制] >>>2
相当于
1011[二进制] >>>2
1011[二进制] >>>2 右移2位
会只剩下高2位数,即 0010[二进制]
再回到我们上边的代码,即:
(h = key.hashCode()) ^ (h >>> 16)
也就是说它这一步是将得到的hashCode
值,对高16位
(右移了16位得到的),然后再与原来的hashCode
值进行了^
运算。
暂时再补充一点^
的介绍,它叫做异或运算
,关于异或运算的特点,可以看我的这个文章java中异或运算的应用,最好是百度一下这个,我也是百度的。这里只需要知道这样的特点即可:
异或的运算方法是一个二进制运算: 1^1=0 0^0=0 1^0=1 0^1=1 两者相等为0,不等为1. 总之,就是这样, n^0=n n^n=0,即任何数与0进行异或,为它本身; 两个相同的数进行异或运算,会得到0。
到了这里,基本的概念我们都理解了,再回来看我们这行代码:
(h = key.hashCode()) ^ (h >>> 16)
大家知道,我们的HashMap中的数组长度
,一般默认是16
,转换成二进制,无非也就是10000
,总共五位二进制数,结合上一篇文章中的介绍,HashMap在存取值的时候,依赖hashCode
值,经过我们这个hash方法
处理后的值,再与数组长度减一进行与运算
确定出元素所处数组的位置,即:tab[i = (n - 1) & hash]
这样的操作,而在进行与运算
的时候,往往因为数组长度减一
这个数太小,造成整个hashCode
值的高位几乎不可能加入到运算中。
因此,为了能够让hashCode
值,高位也能参与到tab[i = (n - 1) & hash]
这样的与运算
中,所以有了这样的右移
,而我觉得,正是因为hashCode
值是int
类型,最大为32
位,因此尽可能的右移最大让所有数加入到运算中,却又不丢失数据
的操作,那就只能是16
这个数!!!
而异或运算^
我理解的是,起到了打乱的作用,高16
位和低16
进行了这样的运算后,会使hash
值出现相同的情况更低,减少了hash
值的碰撞,进一步使HashMap
数据在数组中的落点更加均匀,拥有更大的利用率。关于异或运算^
仅是我理解的这样,如果有大神觉得不对,请帮我纠错下,谢谢了。