如果要理解TreeMap的源码,那就先要理解红黑树的各种操作
红黑树
-
定义
- 红黑树实质上就是一颗自平衡的
二叉查找树
,有时候形态不一定是平衡二叉树,满足以下的条件(左子树节点值<根节点值<右子树节点值) - 引入带颜色的节点也是为了方便在进行插入或删除时,如果破坏了二叉树的平衡性能通过一系列变换保持平衡
- 红黑树的查找、插入、删除的时间复杂度均为
Olog
2n - 适用于频繁插入、删除的场景,实用性更强。
- 红黑树实质上就是一颗自平衡的
-
性质
- 节点是红的或黑的
- 根节点是黑色
- 叶子节点(也叫外部节点、NULL节点、失败节点)是黑色
- 不存在两个相邻的红节点
- 从任意节点到可达的叶子节点的每个路径包含相同数目的黑色节点
-
口诀
- 大小左根右,根叶结点黑(性质2+3),父子不红红(性质4),路路同黑数(性质5)。
-
先查找,确定插入位置(原理同二叉排序树),插入新节点
-
新节点是根----染成黑色
-
新节点是非根----染成红色
- 若插入新节点后依然满足红黑树定义,则插入结束
- 若插入新节点后不满足红黑树定义需要调整,使其重新满足红黑树的定义
- 黑叔:旋转+染色
- LL型:右单旋,父换爷+染色
- RR型:左单旋,父换爷+染色
- LR型:左、右双旋 父换爷+染色
- RL型:右、左双旋,儿换爷+染色
- 红叔:染色+变新
- 叔父爷染色,爷变为新节点
- 黑叔:旋转+染色
转载一篇关于红黑树的一篇文章,非常的浅显易懂
开始我们今天的正题了
TreeMap
的源码解析
首先你要明白以下的属性
这个属性是
TreeMap
集合实现能进行排序的关键private final Comparator<? super K> comparator;
TreeMap
存储的节点private transient Entry<K,V> root;
TreeMap
的长度private transient int size = 0;
TreeMap
的修改次数private transient int
modCount
= 0;//静态内部类,定义了每个节点的详细信息
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;}
Java
中要使用一个对象,那就必须创建一个对象,创建一个对象可以使用工厂模式创建,而最为常见的创建对象的方法是通过new
所有来看TreeMap
的的构造器
//无参构造器
public TreeMap() {
comparator = null;
}
//这是重新比较器后把比较器复制给TreeMap对象
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//调用了putAll(m);
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}
创建对象后,就是添加数据的方法了,put
方法
public V put(K key, V value) {
//创建一个根节点
Entry<K,V> t = root;
if (t == null) {
//调用比较方法
compare(key, key); // type (and possibly null) check
//创建头结点
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
//这里开始判断你使用的是泛型中的比较器,还是TreeMap中比较器
// split comparator and comparable paths
//TreeMap比较器的优先级更加的高
Comparator<? super K> cpr = comparator;
//得到Parent节点的地址
if (cpr != null) {
//把根节点与放入的节点进行比较
do {
//主要的作用是获取插入节点父节点的位置
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
//相同的话,就返回oldValue
return t.setValue(value);
} while (t != null);
}
//这是相同的道理,就是比较器对象不同而已
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
//得到之前的cmp然后把该节点放在对应的位置
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//这个方法就是插入后,调整位置,保持红黑树的方法
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
compare
//比较大小,判断次节点在左节点还是在右节点
@SuppressWarnings("unchecked")
final int compare(Object k1, Object k2) {
//判断是否重写了比较方法,如果没有重写比较方法的话,就去父类中寻找比较方法
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
//使用重写的比较方法
: comparator.compare((K)k1, (K)k2);
}
fixAfterInsertion
//得到该节点的颜色
private static <K,V>
boolean colorOf
(Entry<K,V> p) {
return (p == null ? BLACK :p.color
);
}
得到该节点的父节点
private static <K,V> Entry<K,V>
parentOf
(Entry<K,V> p) {
return (p == null ? null:p.parent
);
}
给节点设置颜色的方法
private static <K,V> void
setColor
(Entry<K,V> p,boolean c
) {
if (p != null)
p.color
= c;
}
得到该节点的左节点
private static <K,V> Entry<K,V>
leftOf
(Entry<K,V> p) {
return (p == null) ? null:p.left
;
}
得到该节点的右节点
private static <K,V> Entry<K,V>
rightOf
(Entry<K,V> p) {
return (p == null) ? null:p.right
;
}
怎么重新调整为红黑树可以参考红黑树的插入
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
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 {
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
rotateLeft(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateRight(parentOf(parentOf(x)));
}
} else {
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))) {
x = parentOf(x);
rotateRight(x);
}
setColor(parentOf(x), BLACK);
setColor(parentOf(parentOf(x)), RED);
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}