PS:由于文档是我在本地编写好之后再复制过来的,有些文本格式没能完整的体现,故提供下述图片,供大家阅览,以便有更好的阅读体验:
HashMap中红黑树TreeNode的split方法源码解读
分析HashMap$TreeNode(既是树又是链表)的split方法的源码,会发现主要分两部分操作:
1. 数据从旧数组转移到新数组上来時,旧数组上的数据会根据(e.hash & oldCap) 是否等于0,重新rehash计算其在新数组上的索引位置,分成2类:
① 等于0时,则将该树链表头节点放到新数组时的索引位置等于其在旧数组时的索引位置,记未低位区树链表lo开头-low;
② 不等于0时,则将该树链表头节点放到新数组时的索引位置等于其在旧数组时的索引位置再加上旧数组长度,记为高位区树链表hi开头high(具体推导过程,详见《HashMap扩容时的rehash方法中(e.hash & oldCap) == 0源码解读》).
2. 当红黑树被split分割开成为两个小红黑树后:
① 当低位区小红黑树元素个数小于等于6时,开始去树化untreeify操作;
② 当低位区小红黑树元素个数大于6且高位区红黑树不为null时,开始树化操作(赋予红黑树的特性)
3. 具体,详见下述的源码解析:
1./*HashMap$TreeNode的split()方法/
方法概述:将红黑树从旧数组转移到新数组
/**
* Splits nodes in a tree bin into lower and upper tree bins,
* or untreeifies if now too small. Called only from resize;
* see above discussion about split bits and indices.
*
* @param map the map
* @param tab the table for recording bin heads
* @param index the index of the table being split
* @param bit the bit of hash to split on(其实就是旧数据的容量大小oldCap)
*/
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
TreeNode<K,V> b = this;
// Relink into lo and hi lists, preserving order
TreeNode<K,V> loHead = null, loTail = null;
TreeNode<K,V> hiHead = null, hiTail = null;
int lc = 0, hc = 0;
for (TreeNode<K,V> e = b, next; e != null; e = next) {
next = (TreeNode<K,V>)e.next;
e.next = null;
if ((e.hash & bit) == 0) {
//区分高低位树链表的核心算法
if ((e.prev = loTail) == null) //低位尾部标记为null,表示还未开始处理,此时e是第一个要处理的低位树链表节点,故e.prev等于loTail都等于null.
loHead = e; //低位树链表的第一个树链表节点
else
loTail.next = e;
loTail = e;
++lc; //低位树元素个数计数
}
else {
if ((e.prev = hiTail) == null)
hiHead = e; //高位树链表的第一个树链表节点
else
hiTail.next = e;
hiTail = e;
++hc; //高位树链表元素个数计数
}
}
if (loHead != null) {
//低位树链表不为null
if (lc <= UNTREEIFY_THRESHOLD) //低位树链表元素个数若小于等于6
tab[index] = loHead.untreeify(map); //开始去树化操作(就是将元素ThreeNode节点 类型都替换成Node节点类型),具体方法源码详见第2点
else {
tab[index] = loHead;
if (hiHead != null) // (else is already treeified) //若高位树链表头节点为空,说明还未处理完高位,还不能开始树化操作
loHead.treeify(tab); //低位树链表元素个数若大于6且高位树链表头节点不等于null,开始将低位树链表真正树化成红黑树(前面都只是挂着TreeNode的名号,但实际只是链表结构,还没包含红黑树的特性,在这里才赋予了它红黑树的特性) 具体方法源码详见[《HashMap之链表转红黑树(树化 )-treefyBin方法源码解读》](https://editor.csdn.net/md/?articleId=106801839)
}
}
if (hiHead != null) {
//原理同低位树链表处理,不再赘述
if (hc <= UNTREEIFY_THRESHOLD)
tab[index + bit] = hiHead.untreeify(map);
else {
tab[index + bit] = hiHead;
if (loHead != null)
hiHead.treeify(tab);
}
}
}
2. /*HashMap$TreeNode的untreeify ()方法/
方法概述:将链表的TreeNode类型转换成Node节点类型
/**
* Returns a list of non-TreeNodes replacing those linked from
* this node.
*/
final Node<K,V> untreeify(HashMap<K,V> map) {
Node<K,V> hd = null, tl = null;
for (Node<K,V> q = this; q != null; q = q.next) {
Node<K,V> p = map.replacementNode(q, null); //将节点的类型转换成Node链表节点类型, 具体方法源码详见第3点
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
}
3. /*HashMap的replacementNode ()方法/
方法概述:将节点p的类型转换成Node链表节点类型
// For conversion from TreeNodes to plain nodes
Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
return new Node<>(p.hash, p.key, p.value, next); //创建Node类型节点并返回
}