写这篇文章真的不是为了水博客,作者本人正在写java数据结构相关的文章,也在积极找工作和准备面试,看到好多面试视频里对于 HashMap 扩容和红黑树升级退化的条件说的含糊其辞,于是做了一个汇总。Talk is cheap, I will show you the code.
在执行 put 方法时
//在put时会执行putVal()方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
... ...
//数组为空,或者数组长度为0会扩容
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
... ...
//键值对的个数大于阀值会扩容,这个size是HashMap的成员变量表示k-v映射的数量
if (++size > threshold)
resize();
... ...
}
/**
* The number of key-value mappings contained in this map.
*/
transient int size;
红黑树发生升级会先检查条件
//升级红黑树时
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
//数组长度小于64,会进行一次扩容代替升级
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if() {
//链表升级红黑树
}
}
static final int TREEIFY_THRESHOLD = 8;
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
......
/*
* p是链表的头结点,for循环的第一次, e = p.next 的时候 e 指向链表的第二个元素,
* 此时 binCount = 0,下面升级红黑树的条件是 binCount >= 7。for循环继续走下去,
* 在链表中第九个元素为空的时候 binCount = 7,此时第九个元素刚刚已经插入元素。
* 那么链表长度大于8时执行treeifyBin()方法,会先判断Node 数组长度大于等于64
* 不满足则会发生扩容代替升级,源码在 红黑树升级条件 的标题的紧挨着的上面
*/
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
}
......
}
//扩容resize时,会把红黑树拆分成hi和lo两颗红黑树,
//hc和lc分别是highCount和lowCount的缩写,表示两颗树的结点个数
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
... ...
//lc小于等于6,也就是lo这颗树的结点个数小于等6发生退化
if (lc <= UNTREEIFY_THRESHOLD)
tab[index] = loHead.untreeify(map);
... ...
//hc小于等于6,也就是hi这颗树的结点个数小于等6发生退化
if (hc <= UNTREEIFY_THRESHOLD)
tab[index + bit] = hiHead.untreeify(map);
... ...
}
//remove时
//如果红黑树根root为空,或者root的左子树/右子树为空,
//root.left.left根的左子树的左子树为空,红黑树会退化成链表
if (root == null
|| (movable
&& (root.right == null
|| (rl = root.left) == null
|| rl.left == null))) {
tab[index] = first.untreeify(map); // too small
return;
}