JDK8中对hash算法和寻址算法的优化
1. 寻址算法
在插入和查找数据的时候,会根据一个 key 得到对应的 hash 值(传入的参数,已知),根据这个 hash 值进行得到元素在数组的下标位置,这个计算过程就是寻址算法。
/**
* Implements Map.get and related methods.
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
- 我们知道了一个 key 的 hash 值,用这个 hash 值根数组长度取模,就可以得到下标位置,实际在代码中,采用了与运算 ( 两个都是 1 ,结果为 1 ,否则为 0 )代替了取模运算
- 公式: ( n -1 ) & hash 其中 n 为数组长度
为什么用与运算?
- 对于现代的处理器来说,除法和求余数(模运算)是最慢的动作
- 数学公式:
a % b = ( b -1 ) & a, 当b是2的指数时,等式成立
2. hash哈希算法
- 寻址算法:hash -> index 需要根据 hash 值计算数组下标
- 哈希算法:key.hashCode() -> hash 就是根据一个 key ,通过计算得到它对应的 hash 值
static final int hash(Object key){
int h;
return (key == null)? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
- 为什么不直接使用它的hashCode?
如果直接使用 hashcode 作为 hash 值
寻址算法: ( n - 1 ) & hash
条件 | 地址 |
---|---|
A 的 hashCode : | 1000 0100 0111 0001 0000 0111 1000 0000 |
n - 1 = 15 | 0000 0000 0000 0000 0000 0000 0000 1111 |
与运算结果 | 0000 0000 0000 0000 0000 0000 0000 1111 |
B 的 hashCode | 0111 0111 0011 1000 1010 0001 0100 0000 |
n - 1 = 15 | 0000 0000 0000 0000 0000 0000 0000 1111 |
与运算结果 | 0000 0000 0000 0000 0000 0000 0000 1111 |
运算得出来的结果相同,这样的散列结果太让人失望了,这很明显不是一个好的散列算法
把 hashcode 向右移 16 位,然后再和原来的 hashcode 做异或运算 (相同为 0 , 不同为 1)
条件 | 地址 |
---|---|
A 的 hashCode : | 1000 0100 0111 0001 0000 0111 1000 0000 |
右移 16 位 | 0000 0000 0000 0000 1000 0100 0111 0001 |
异或运算 | 1000 0100 0111 0001 1000 0011 1111 0001 |
n - 1 = 15 | 0000 0000 0000 0000 0000 0000 0000 1111 |
与运算结果 | 0000 0000 0000 0000 0000 0000 0000 0001 |
B 的 hashCode | 0111 0111 0011 1000 1010 0001 0100 0000 |
右移 16 位 | 0000 0000 0000 0000 0111 0111 0011 1000 |
异或运算 | 0111 0111 0011 1000 1101 0110 0100 1000 |
n - 1 = 15 | 0000 0000 0000 0000 0000 0000 0000 1111 |
与运算结果 | 0000 0000 0000 0000 0000 0000 0000 1000 |
通过哈希算法的优化,一定程度避免了 hash 冲突,让数据更加散列。
hashCode相关:
https://zhuanlan.zhihu.com/p/33915892
https://www.cnblogs.com/dolphin0520/p/3681042.html