JDK1.8源码泛读之Hashmap的hash算法

hash的核心方法之一就是键key的hash值的算法,因为这牵扯到键值对的桶查找,从源码看有以下两个地方最终决定了hash值的大小:

 1     public V put(K key, V value) {
 2         return putVal(hash(key), key, value, false, true);
 3     }
 4 
 5     //获取对象第一次hash值
 6     static final int hash(Object key) {
 7         int h;
 8         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
 9     }
10 
11     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
12                    boolean evict) {
13         Node<K,V>[] tab; Node<K,V> p; int n, i;
14         if ((tab = table) == null || (n = tab.length) == 0)
15             n = (tab = resize()).length;
16         // 根据hash值 取到hash表的index (n - 1) & hash
17         if ((p = tab[i = (n - 1) & hash]) == null)
18             tab[i] = newNode(hash, key, value, null);
19         else {
20         .....省略
21         }
22      }

 截取以上源码从key,到找到hash桶的index一共经历了两步:
1、(h = key.hashCode()) ^ (h >>> 16)
h>>>16 无符号向右移16位,相当于int类型的hashcode和自身无符号右移的进行了一次异或操作。
相当于混合了原始hashcode高低16位,从而达到扰动作用。

比如:
h=hashcode():11111111 11111111 11110000 11101010
异或
h>>>16        :00000000 00000000 11111111 11111111
第一步结果=   11111111 11111111 00001111 00010101

2、(n - 1) & hash
第一步结果和hashmap长度进行与操作,而hashmap在设计之初,就把长度n设计成了2的整数次幂,
因为这样,正好相当于一个“低位掩码”,比如n为16,那么二进制标识为:
00000000 00000000 00000000 00001111

再与第一步结果与操作:
11111111 11111111 00001111 00010101
00000000 00000000 00000000 00001111  &
00000000 00000000 00000000 00000101
= 高位全部清零:101 即结果为5

(n-1)& hash的结果正好都是分布在n的范围内。

posted @ 2018-03-13 20:29  Atomm  阅读(216)  评论(0编辑  收藏  举报