[HashMap源码学习之路]---put方法中的hash方法介绍

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 ,这个概念,没毛病吧?
《[HashMap源码学习之路]---put方法中的hash方法介绍》
  那上边的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 值进行了^ 运算。
《[HashMap源码学习之路]---put方法中的hash方法介绍》
  暂时再补充一点^ 的介绍,它叫做异或运算 ,关于异或运算的特点,可以看我的这个文章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 数据在数组中的落点更加均匀,拥有更大的利用率。关于异或运算^ 仅是我理解的这样,如果有大神觉得不对,请帮我纠错下,谢谢了。

    原文作者:JAVA码上飘
    原文地址: https://blog.csdn.net/wohaqiyi/article/details/81195717
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞