public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
HashMap继承抽象类AbstractMap,实现了NavigableMap接口,NavigableMap继承自SortedMap,方法有lowerEntry小于,floorEntry小于等于,ceilingEntry大于,higherEntry大于等于,便于搜索查找。
- TreeMap的属性
private final Comparator<? super K> comparator; //保存顺序的比较器,
private transient Entry<K,V> root = null; //根节点
private transient int size = 0; //树中的节点数量
private transient int modCount = 0;
private static final boolean RED = false;
private static final boolean BLACK = true;
- TreeMap的构造方法
public TreeMap() {
comparator = null; //comparator为空,即采用自然顺序维持TreeMap中节点的顺序
}
public TreeMap(Comparator<? super K> comparator) { //带有比较器的构造方法
this.comparator = comparator;
}
public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m); //调用putAll方法将Map中的所有元素加入到TreeMap中
}
public TreeMap(SortedMap<K, ? extends V> m) { //根据SortedMap的比较器维持TreeMap中的节点顺序
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null); //SortedMap中的内容添加到TreeMap中
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
- 静态内部类
static final class Entry<K,V> implements Map.Entry<K,V> {
K key; //键
V value; //值
Entry<K,V> left = null; //左节点
Entry<K,V> right = null; //右节点
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;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
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;
}
}
put
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) { //根节点为null,则直接插入根节点
compare(key, key);
root = new Entry<>(key, value, null); //创建个新的entry节点,指向root
size = 1; //当前容量是1
modCount++;
return null;
}
int cmp; //cmp表示key排序的返回结果
Entry<K,V> parent;
Comparator<? super K> cpr = comparator; //有指定的比较器
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
return t.setValue(value);
} while (t != null);
}
else { //默认的比较器
if (key == null)
throw new NullPointerException();
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); //新建红黑树节点
if (cmp < 0) //小于parent的key,则当做左子节点
parent.left = e;
else
parent.right = e; //大于parent的key,则当做右子节点
fixAfterInsertion(e); //恢复红黑树的特性
size++;
modCount++;
return null;
}
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
/** From CLR */
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) { //循环 节点不为null且不为根节点且节点的父节点是红色
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //当前节点的父节点是当前节点的爷爷节点的的左节点(叔叔节点)
Entry<K,V> y = rightOf(parentOf(parentOf(x))); //获取当前节点的叔叔节点(爷爷有且只有2个孩子,左是父节点,右是叔节点)
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; //根节点设置为黑色
}
private void rotateLeft(Entry<K,V> p) { //左旋
if (p != null) {
Entry<K,V> r = p.right;
p.right = r.left;
if (r.left != null)
r.left.parent = p;
r.parent = p.parent;
if (p.parent == null)
root = 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;
}
}
添加一组数据来讲解吧,注释写不下去了-_-!
eg:
TreeMap m = new TreeMap();
m.put(2,1);
m.put(3,2);
m.put(5,3);
m.put(6,4);
m.put(8,5);
m.put(9,6);
m.put(10,7);
m.put(1,8);
按顺序添加,先添加key为2的数据,put方法,root为null,创建一个节点,节点的color属性默认是黑色,这次2是根节点,如下图
添加key为3后,如下图
默认的比较器,比较key,3比2大,所以放到右边。fixAfterInsertion后,将key设置为红色,root节点设置为黑色。
添加key为5后,如下图
比较key,5比2大,所以放到右边,比3也大,就放3右边了,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))=null,parentOf(x)是key为3的节点,
进入到else判断,leftOf(parentOf(parentOf(x)))=null,colorOf(y)=BLACK进入else判断,leftOf(parentOf(x))=null,不会进入到if判断,将当前节点5的父
节点3设置为黑色,将爷爷节点2设置为红色,进行左旋操作,左旋是将根节点2作为参数,传进去的,左旋后,3变成了根节点,2变成了3的左子节点。
添加key为6后,如下图
比较key,6比3大,所以放到右边,比5也大,就放5右边了,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))=null,parentOf(x)是key为5的节点,
进入到else判断,leftOf(parentOf(parentOf(x)))是节点2,color是红色的,colorOf(y) == RED进入if判断,将当前节点6的父节点5设置为黑色,将节点2设置为黑色,
将当前节点6的爷爷节点3设置为红色,将爷爷节点指向3,跳出循环,设置根节点为黑色。
添加key为8后,如下图
比较key,8比3大,放到右边,比5也大,放右边,比6也大,放6右边,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))=null,
parentOf(x)是key为6的节点,进入到else判断,leftOf(parentOf(parentOf(x)))是节点2,color是黑色的,进入else判断,leftOf(parentOf(x)为null,
不等于当前x节点,将当前节点8的父节点6设置为黑色,将爷爷节点5设置为红色,进行左旋操作,左旋是将爷爷节点5作为参数,传进去的,左旋后,5变成了6的左子节点,6变成3的右节点。
添加key为9后,如下图
比较key,9比3大,放到右边,比6大,放右边,比8大,放右边,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x))是key为5的节点,parentOf(x)是key为8的节点,进入到else判断,leftOf(parentOf(parentOf(x)))是节点5,color是红色,进入if判断,将当前x节点的父节点8设置为黑色,将节点5设置为黑色,将当前节点的爷爷节点
6设置为红色,将爷爷节点6指向当前插入节点9,此时x节点是节点6了,x.parent.color是黑色,所以跳出循环,设置根节点为黑色。
添加key为10后,如下图
比较key,10比3大,放到右边,比6大,放右边,比8大,放右边,比9大,放右边,fixAfterInsertion后,if判断leftOf(parentOf(parentOf(x)),key8的左子节点是空,parentOf(x)是key为9的节点,进入到else判断,leftOf(parentOf(parentOf(x)))是空,进入else判断,leftOf(parentOf(x))是空,不等于当前节点x,设置当前节点10的父节点9为黑色,当前节点10的爷爷节点8为红色,进行左旋操作,左旋是将爷爷节点8作为参数,传进去的,左旋后,8变成了9的左子节点,9变成6的右节点。
添加key为1后,如下图
比较key,1比3小,放到左边,比2小,放左边,fixAfterInsertion后,x.parent.color,x.parent是节点2,是黑色的,所以没进入循环,设置根节点是黑色就结束了。
总结下特点:
1.根节点总是黑色。
2.新插入的节点是默认是黑色,在经过fixAfterInsertion方法后,会变成红色。
3.fixAfterInsertion方法:当前节点不是空,不是根节点,父节点是红色才会进行调整操作。(一个路径上不能有2个红色相邻的节点)
4.根节点到所有叶子节点的数量必须相同(上例put方法,key为5,为了保持平衡进行左旋操作)
remove
public V remove(Object key) {
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p);
return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
if (p.left != null && p.right != null) { //被删除节点的左子节点和右子节点都不为空,则用p节点的中序后继节点代替p节点
Entry<K,V> s = successor(p); //寻找P的替代节点
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
Entry<K,V> replacement = (p.left != null ? p.left : p.right); //替代节点,左子节点不能空则优先使用左子节点,否则就用右子节点
if (replacement != null) { //替代节点不为空
replacement.parent = p.parent;
if (p.parent == null)
root = replacement; //p节点没有父节点,替代节点作根节点
else if (p == p.parent.left)
p.parent.left = replacement; //P为左节点,则用replacement来替代为左节点
else
p.parent.right = replacement; //P为右节点,则用replacement来替代为右节点
p.left = p.right = p.parent = null; //P节点从这棵树中剔除掉
if (p.color == BLACK)
fixAfterDeletion(replacement); //P为黑色,则需要调整红黑树
} else if (p.parent == null) { // return if we are the only node.
root = null; //P为根节点,直接删除
} else { //P节点不存在子节点,直接删除
if (p.color == BLACK)
fixAfterDeletion(p); //P节点的颜色为黑色,调整红黑树
if (p.parent != null) { //删除P节点
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
if (t == null)
return null;
else if (t.right != null) { //寻找右子节点的最左子节点
Entry<K,V> p = t.right;
while (p.left != null)
p = p.left;
return p;
} else { //寻找左子节点的最右子节点
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
/** From CLR */
private void fixAfterDeletion(Entry<K,V> x) {
while (x != root && colorOf(x) == BLACK) {
if (x == leftOf(parentOf(x))) {
Entry<K,V> sib = rightOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
sib = rightOf(parentOf(x));
}
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(rightOf(sib)) == BLACK) {
setColor(leftOf(sib), BLACK);
setColor(sib, RED);
rotateRight(sib);
sib = rightOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(rightOf(sib), BLACK);
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
Entry<K,V> sib = leftOf(parentOf(x));
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}
if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}
setColor(x, BLACK);
}
eg:
TreeMap m = new TreeMap();
m.put(2,1);
m.put(3,2);
m.put(5,3);
m.put(6,4);
m.put(8,5);
m.put(9,6);
m.put(10,7);
m.put(1,8);
m.remove(3);
m.remove(9);
m.remove(1);
删除根节点3,如下图
判断节点3,有没有左子节点和右子节点,优先寻找右子节点的最左子节点,没有右子节点,则寻找寻找左子节点的最右子节点,节点3的右子节点的最左节点是5,节点5作为替换节点,没有左子节点和右子节点,节点5是黑色,进行调整操作,调用fixAfterDeletion方法,调整红黑树结构。
删除节点9,如下图
节点9没有右节点的最左子节点,那么就取左节点的最右子节点了,节点8刚好是黑色的,进行调整操作,调用fixAfterDeletion方法,调整红黑树结构。
删除节点1,如下图
节点1没有左右子节点,且刚好是红色的,直接删除了。
总结下删除的3种情况:
- 待删除节点没有子节点,直接删掉。
- 待删除节点只有一个子节点,直接删除。
- 待删除的子节点是黑色,子节点修改成黑色。
- 待删除的子节点是红色,直接删除。
- 待删除节点有2个子节点
- 找出替换节点,优先寻找待删除节点的右子节点的最左子节点,找不到的话,就左子节点的最右子节点。
- 调整待删除节点的父节点和替换节点的关系。
- 调整替换节点的子节点和被删除节点子节点的关系。