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的范围内。