1、什么是红黑树?
红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。 在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 结点是红色或黑色。
性质2. 根结点是黑色。
性质3. 所有叶子都是黑色。(叶子是NIL结点)
性质4. 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
性质5. 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。
3、主要方法
treeifyBin方法 ——列表进行树化
/*
* Replaces all linked nodes in bin at index for given hash unless table is too small, in which case resizes instead.
*/
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();
//以防万一 在确定一下 你传的这个位置上是不是空的 顺便给e赋值
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
// 将每个节点包装成TreeNode。
TreeNode<K,V> p = replacementTreeNode(e, null);
//下面这一步 就是熟悉的 将单链表 变成双向链表
//然后交给treeify进行树化了
if (tl == null)
hd = p;
else {
// 将所有TreeNode连接在一起此时只是链表结构。
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
//执行完上面的循环 则这个列表是双向的列表
//下面这个判断 在进行 判断 那个位置 是否为空
if ((tab[index] = hd) != null)
// 对TreeNode链表进行树化。
hd.treeify(tab);
}
}
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);
}
}
treeify方法——将节点变成树
final void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
//利用当前穿过来的这个 this 进行遍历 前面说了 low 和hig 只用到了 prev 和next 更像是列表
//所以这个方法是让他们彻底变成真正得树
for (TreeNode<K,V> x = this, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
if (root == null) {
x.parent = null;
x.red = false;
root = x;
}
//root不为空,根节点已经存在了,处理剩下对象
else {
K k = x.key;
int h = x.hash;
Class<?> kc = null;
// 此时红黑树已经有了根节点,上面获取了当前加入红黑树的项的key和hash值进入核心循环。
// 这里从root开始,是以一个自顶向下的方式遍历添加。
// for循环没有控制条件,由代码内break跳出循环。
for (TreeNode<K,V> p = root;;) {
// dir:directory,比较添加项与当前树中访问节点的hash值判断加入项的路径,-1为左子树,+1为右子树。
int dir, ph; // ph:parent hash。
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
TreeNode<K,V> xp = p; // xp:x parent
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
// 如果xp的hash值大于x的hash值,将x添加在xp的左边。
if (dir <= 0)
xp.left = x;
else
xp.right = x;
// 维护添加后红黑树的红黑结构。
root = balanceInsertion(root, x);
// 跳出循环当前链表中的项成功的添加到了红黑树中
break;
}
}
}
}
//再次确定根节点
moveRootToFront(tab, root);
}
split方法——扩容时将树进行拆分
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
//tab是newtable 不是oldtable
//index 时 咱要拆分的那个树所在 坐标
//bit 是 oldcap容量
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;
//这一步 也是和 前面的列表操作类似 也是 将 里面的树 进行区分位low 和hig
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;
}
}
//执行完这操作 就分成了两颗树 说是树但是 low和hig 其实只是在用 pre 和nex 更像是 列表
//然后 看看low这棵树 元素熟路是否小于等于UNTREEIFY_THRESHOLD(默认6)
if (loHead != null) {
if (lc <= UNTREEIFY_THRESHOLD)
//如果数量太少则进行链化
tab[index] = loHead.untreeify(map);
else {
//没有则满足要求 进行位置的移动即可
tab[index] = loHead;
if (hiHead != null) // (else is already treeified)
//将这个low树进行重新树化
// 为什么是树了还要树化? 上面说了 他们只用到了prev和next 完全不是树 所以需要进行树化 之所以叫他们说是因为他们是treenode类
//至于为什么 需要判断hiHead != null 才能进行操作 是为了提高效率
//比如你想一下你搞半天 hig那个树 啥也没有 不就是证明 都在low上面 那还需要重新树化什么 直接搞过去就行
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);
}
}
}
untreeify方法——(将节点变成链表)
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);
if (tl == null)
hd = p;
else
tl.next = p;
tl = p;
}
return hd;
}