分析java集合的时候,HashMap的存储优化和TreeMap的底层都使用到了红黑树
来看一看红黑树调整的具体源码。
版本:jdk1.8
//首先是一个Entry类(TreeMap的内部类)
//包含,key、value、左孩子引用、右孩子引用、父节点引用、结点颜色(默认为黑色结点)
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;
//有参构造
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
//重写了equals、hashcode、toString方法
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}
public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}
public String toString() {
return key + "=" + value;
}
}
//TreeMap内的方法
/**
关键就是理解左旋与右旋的方式与染色,结点指向的改变等,代码参考图示就可理解
**/
//左旋
private void rotateLeft(Entry<K,V> p) {
if (p != null) {
Entry<K,V> r = p.right;//p的右节点
p.right = r.left;//让p的右结点指向原来r的左节点
if (r.left != null)//不等于null要设置parent
r.left.parent = p;
r.parent = p.parent;//更改父指针
if (p.parent == null)
root = r;//r变为根节点
else if (p.parent.left == p)
p.parent.left = r;
else
p.parent.right = r;
r.left = p;
p.parent = r;
}
}
//右旋
private void rotateRight(Entry<K,V> p) {
if (p != null) {
Entry<K,V> l = p.left;
p.left = l.right;
if (l.right != null) l.right.parent = p;
l.parent = p.parent;
if (p.parent == null)
root = l;
else if (p.parent.right == p)
p.parent.right = l;
else p.parent.left = l;
l.right = p;
p.parent = l;
}
}
//插入后调整
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;//新插入的结点颜色置为红色
while (x != null && x != root && x.parent.color == RED) {//父节点为红色
//若x的父节点==x的爷结点的左节点(也就是L)
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//找到叔叔结点
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {//如果是红色
setColor(parentOf(x), BLACK);//父染色
setColor(y, BLACK);//叔染色
setColor(parentOf(parentOf(x)), RED);//爷染色
x = parentOf(parentOf(x));//将爷结点看作新结点
} else {
//否则,叔为黑色,需要旋转调整
//如果子结点为父节点的右节点(连着上面,也就是LR)
if (x == rightOf(parentOf(x))) {
x = parentOf(x);//此时x为插入结点的父节点
rotateLeft(x);//先左旋
}
//子结点为父节点的左节点(连着上面,也就是LL)
//当然,上面的执行完上面的LR中的if,也要执行下面。
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));//右旋
}
} else {
//父为R
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {//红叔
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {//黑叔
if (x == leftOf(parentOf(x))) {//RL
x = parentOf(x);
rotateRight(x);//右旋
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));//左旋
}
}
}
root.color = BLACK;
}
红黑树的调整:
NOTE:叔叔为父节点的兄弟结点,我们将空域也看作一个结点,爷结点为父节点的父节点。
1、插入的为根节点,直接染黑
2、否则染为红色,插入
若破坏了红黑树的特性,需要染色
- 若叔叔结点为红色
- 叔结点,父节点,爷结点染色(都变为与原来相反的颜色)---->爷结点变为新结点(也就是把爷结点当作新插入的结点),重复上述步骤
- 若叔叔结点为黑色
- LL旋转,旋转完,将父和爷染色【以父结点为旋转中心】
- RR旋转,------------------
- LR旋转,旋转完,将新节点与爷结点染色【先以父结点为旋转中心,再以新结点为旋转中心】
- RL旋转,-------------------------
调整过程图示: