关于HashMap的一些问题

1.为什么Entry数组大小必须为2的幂

// JDK 1.7
static int indexFor(int h, int length) {
    return h & (length-1);
}
  1. 使用减法替代取模,提升计算效率;
  2. 为了使不同 hash 值发生碰撞的概率更小,尽可能促使元素在哈希表中均匀地散列。

我们知道,在计算机中,0与任何值相&都是0;1与任何值相&则取决于值本质,是0则是0,是1则还是1。同时,取余运算的效率远不如与运算

知道这两点后,我们用与运算代替取模运算,h&(length-1) 就相当于 h%length,而效率大大提升。此时如果传入的是 length 是 2 的次幂,即偶数,则减 1 后变为奇数,二进制位之后都是 1,此时无论传入的 hash 值是奇数还是偶数,最后相与得到的结果是不确定的,这就保证了散列表的均匀性。如果传入的 length 是奇数,则减 1 后变为偶数,此时最后一位永远是 0,此时无论传入的 hash 值是奇数还是偶数,最后的结果永远都是偶数,这样任何 hash 值都只会被散列到数组的偶数下标位置上,这便浪费了近一半的空间

2.如何进行 hash 运算的

即使散列表很松散,但是因为与 length 的低位相&,所以只能是最后几位,也很容易发生碰撞,这个时候就需要使用 hash 算法

JDK 1.7

// JDK 1.7
inal 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);
}

扰动函数是为了解决hash碰撞的。它会综合hash值高位和低位的特征,并存放在低位,因此在与运算时,相当于高低位一起参与了运算,以减少hash碰撞的概率

在 JDK 1.7 中,使用了 4 次向右移位,就是为了将高位的值与低位的值混合。同时进行了 4 次扰动,目的就是为了增加低位值的随机性,从而使散列分布的更加松散,以此提高性能

JDK 1.8

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

将计算出来的 hash 值向右边移 16 位,正好是 32 位的一半,此时就将高位移到了低位,将高位和低位进行异或运算,增加了低位的随机性,同时也保持了高位的特征


参考

https://blog.csdn.net/fan2012huan/article/details/51097331
https://www.javazhiyin.com/188.html#m
https://blog.csdn.net/sidihuo/article/details/78489820 https://blog.csdn.net/u011240877/article/details/53351188#6hashmap-的获取方法-get
http://ibat.xyz/2017/02/16/浅聊HashMap中的hash算法/
http://www.importnew.com/7099.html

猜你喜欢

转载自blog.csdn.net/babycan5/article/details/83350189