这个是网上查资料抄的,感谢百度
HashMap 底层的数据结构主要是:数组 + 链表 + 红黑树。其中当链表的长度大于等于 8 时,链表会转化成红黑树,当红黑树的大小小于等于 6 时,红黑树会转化成链表
点进源码看看
Treeify_threshold 为8 就是说节点大于等于8时,会判断是否要转为红黑树
min_treeify_capacity 就是 说 数组容量 大于64时,链表才会转化为红黑树
也就是说: 数组容量大于64,并且链表的节点个数大于8时候,就会把链表改为红黑树
好问题来了:
JDK1.8中HashMap在出现hash碰撞时链表长度超过8就一定会变成红黑树吗
答案是:否.
实际上转换红黑树有个大前提,就是当前hash
table的长度也就是HashMap的capacity(不是size)不能小于64.小于64就只是做个扩容.
Treeify_threshold设置为8是有原因的:
还是点进 Hashmap的源码来看,里面的红黑树节点:
看一下链表的节点:
网上说 TreeNodes 占用空间是 Nodes的两倍,不管对不对,反正红黑树要更加耗费空间,正常情况下是一般不要用红黑树,当我的Entry数组大于 64了,然而链表的节点个数还是大于8,说明一定是hash算法出现问题了,出现了严重的hash碰撞了,所有有必要使用红黑树,将长链表查询O(N) 的复杂度降低为 O(logN)
Treeify_threshold设置为 8是参考了泊松分布
数量 概率
0 : 0.60653066
1 : 0.30326533
2 : 0.07581633
3 : 0.01263606
4 : 0.00157952
5: 0.00015795
6: 0.00001316
7: 0.00000094
8: 0.00000006
按照泊松分布计算,理论上来说,链表长度为8 的概率是 0.00000006,属于小概率事件,正常来说不会转红黑树
负载因子:
负载因子 = 总键值对数 / 箱子数量
哈希表还有 一个重要的属性:负载因子,它是衡量哈希表的空/满程度,一定程度上也能体现查询的效率。其计算公式为:
当负载因子大于 0.75时,就会扩容
负载因子越大,意味着哈希表越满,越容易导致冲突(更大的概念找到同一个箱子上),因而查询效率也就更低。因而,一般来说,当负载因子大于某个常数(可能是1,也可能是其他值,Java8的HashMap的负载因子为0.75)时,哈希表就会自动扩容。
关于 Hashmap 红黑树 的查找的源码
/**
* Finds the node starting at root p with the given hash and key.
* The kc argument caches comparableClassFor(key) upon first use
* comparing keys.
*/
final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
TreeNode<K,V> p = this;
do {
int ph, dir; K pk;
TreeNode<K,V> pl = p.left, pr = p.right, q;
if ((ph = p.hash) > h)
p = pl;
else if (ph < h)
p = pr;
else if ((pk = p.key) == k || (k != null && k.equals(pk)))
return p;
else if (pl == null)
p = pr;
else if (pr == null)
p = pl;
else if ((kc != null ||
(kc = comparableClassFor(k)) != null) &&
(dir = compareComparables(kc, k, pk)) != 0)
p = (dir < 0) ? pl : pr;
else if ((q = pr.find(h, k, kc)) != null)
return q;
else
p = pl;
} while (p != null);
return null;
}
/**
* 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;
}