HashMap红黑树源码深度解析
目录
前言
HaspMap源码深度分析(一)
在上篇博客的分析中,没有涉及到红黑树的分析,因此这里统一对HashMap中的红黑树进行统一分析它的源码。在阅读本篇博客前,是需要提前了解一下红黑树的,主要有红黑树的特性、如何维持红黑树的平衡等。
源码分析
TreeNode分析
hashMap转换为树的过程,首先要将单链表转换为双链表,那节点之间的关系则是靠继承到的LinkedHashMap.Entry中的next来维护的。在经过转换双链表之后,再转换为树,那转换为树的过程并没有删掉节点之间的关联关系,因此HashMap中的红黑树,也保留着双链表的关系。
/**
* 红黑树,这里继承了LinkedHashMap.Entry, 里面维护了next来指向下一个节点,
* 说明由双链表转换为红黑树后,仍然保留了双向链表的关系。
*/
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
//代表节点的父节点
TreeNode<K,V> parent; // red-black tree links
//指向左孩子节点
TreeNode<K,V> left;
//指向右孩子节点
TreeNode<K,V> right;
//指向前一个节点
TreeNode<K,V> prev; // needed to unlink next upon deletion
//标识节点是黑色的,还是红色的。
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
}
root方法分析
root方法的作用是获取红黑树的根节点,比较简单。
/**
* 获取红黑树的根节点。
*/
final TreeNode<K,V> root() {
/*
* this实际是指向调用root()方法的节点,如:e.root(), 那么this指向的就是e。
* 这里的就是从当前节点开始,一直向上遍历,知道遍历节点的父节点为空,那么这个节点
* 就是根节点了。
*/
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p;
}
}
split方法分析
split方法主要发生在resize方法内,用于将旧数组中的红黑树的迁移处理。
/**
* 将红黑树进行拆分
*/
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
//---------------------------------这一块的具体的逻辑与resize中的分析差不多 start************************************
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)
loHead = e;
else
loTail.next = e;
loTail = e;
++lc;
}
else {
if ((e.prev = hiTail) == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
++hc;
}
}
//---------------------------------这一块的具体的逻辑与resize中的分析差不多 start************************************
/*
* 低位的头节点不位空处理流程
*/
if (loHead != null) {
//如果低位树节点的个数小于6,那么就将红黑树转换位链表
if (lc <= UNTREEIFY_THRESHOLD)
tab[index] = loHead.untreeify(map);
//如果低位树节点的个数大于6,那么将新数组的索引指向拆分后的树的头节点,并再次进行红黑树树化的处理,以维持红黑树的平衡
else {
tab[index] = loHead;
if (hiHead != null) // (else is already treeified)
loHead.treeify(tab);
}
}
if (hiHead != null) {
if (hc <= UNTREEIFY_THRESHOLD)
tab[index + bit] = hiHead.untreeify(map);
else {
tab[index + bit] = hiHead;
if (loHead != null)
hiHead.treeify(tab);
}
}
}
treeify方法分析
HashMap由链表转换树的过程,主要的思想就是,从根节点遍历红黑树,以找到新增节点的插入位置并将新增节点插入到红黑树了,最后需要维护红黑树的平衡。
/**
* 将双向链表转换位红黑树。
* 这里需要知道红黑树的一些特性:
* (1)每个节点或者是黑色,或者是红色。
* (2)根节点是黑色。
* (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
* (4)如果一个节点是红色的,则它的子节点必须是黑色的。
* (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
*/
final void treeify(Node<K,V>[] tab) {
//root表示根节点
TreeNode<K,V> root = null;
/*
* 这里需要搞清楚,this实际指向的是调用treeify方法的节点,
* x: 指向当前节点
* next: 指向x的下一个节点
*/
for