在总集篇中我大概梳理了一下整个集合类的关系,这篇文章是对总集篇的扩展,本文将详细讨论TreeMap的实现原理。所有涉及到的源码都是基于JDK11。顺便插一句,大家要学就学新的嘛,毕竟11可是长期支持版本,可以用好几年的那种。
TreeMap简介
TreeMap实现了NavigableMap接口,继承了AbstractMap类,NavigableMap接口定义了TreeMap的相关行为,规定TreeMap需要支持元素有序,支持元素定位,支持元素有序是TreeMap与HashMap的区别,支持程序有序是其与LinkedHashMap的区别。基于这些原因,TreeMap选择了红黑树作为其底层结构,虽然损失了一部分查询性能,但是实现了程序有序的要求。
TreeMap的类描述
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
其中克隆与序列化接口不讨论,主要是看看NavigableMap接口,这个接口扩展了SortedMap接口,所以下面将两个接口中规定重要功能合在一起看:
// SortedMap接口
// 该分支下支持比较器,如果没有指定,则按元素的自然顺序排序
Comparator<? super K> comparator();
// 返回指定范围的子视图,范围左闭右开
SortedMap<K,V> subMap(K fromKey, K toKey);
// 返回指定范围的子视图,从第一个结点开始到指定节点,不包括指定节点
SortedMap<K,V> headMap(K toKey);
// 返回指定范围的子视图,从指定节点到最后一个节点,包括指定节点
SortedMap<K,V> headMap(K toKey);
// 返回第一个key/返回最后一个key
K firstKey();
K lastKey();
Set<K> keySet();
Collection<V> values();
Set<Map.Entry<K, V>> entrySet();
// NavigableMap接口
// 返回比指定key小的元素中的最大元素/key
Map.Entry<K,V> lowerEntry(K key);
K lowerKey(K key);
// 返回小于或等于key的元素中的最大元素/key
Map.Entry<K,V> floorEntry(K key);
K floorKey(K key);
// 返回大于或等于key的元素中最小的元素/key
Map.Entry<K,V> ceilingEntry(K key);
K ceilingKey(K key);
// 返回大于key的元素中的最小元素
Map.Entry<K,V> higherEntry(K key);
K higherKey(K key);
// 返回第一个元素
Map.Entry<K,V> firstEntry();
// 返回最后一个元素
Map.Entry<K,V> lastEntry();
// 返回并删除第一个元素
Map.Entry<K,V> pollFirstEntry();
// 返回并删除最后一个元素
Map.Entry<K,V> pollLastEntry();
// 返回一个反序的视图
NavigableMap<K,V> descendingMap();
// 返回key的一个正序集合
NavigableSet<K> navigableKeySet();
// 返回key的一个反序集合
NavigableSet<K> descendingKeySet();
// 返回子视图,自定义边界的开闭
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive);
NavigableMap<K,V> headMap(K toKey, boolean inclusive);
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
// 返回子视图,边界遵循左开右闭规则
SortedMap<K,V> subMap(K fromKey, K toKey);
SortedMap<K,V> headMap(K toKey);
SortedMap<K,V> tailMap(K fromKey);
TreeMap的成员变量
// 持有指定的比较器
private final Comparator<? super K> comparator;
// 红黑树的根节点
private transient Entry<K,V> root;
// 大小
private transient int size = 0;
// 并发修改感知器,详细介绍请看HashMap,它们套路都是一样的
private transient int modCount = 0;
private transient EntrySet entrySet;
private transient KeySet<K> navigableKeySet;
private transient NavigableMap<K,V> descendingMap;
// 当submap有未包含的边界时,将其转化为该空Object,防止迭代器中包含null
private static final Object UNBOUNDED = new Object();
// 红黑树节点
private static final boolean RED = false;
private static final boolean BLACK = true;
支撑TreeMap的内部数据结构
老规矩,先给张图
首先,把SubMap去掉,这个不讨论,它的作用是为了解决一些历史遗留问题,文档中明确说明不要用这个东西。之后可以按KeySet,Values,EntrySet分为与之配套的Iterator,spliterator。还有为了支持submap的相关数据结构以及支持正反序的串、并行迭代器。以及作为数据节点的Entry。
Entry的实现
static final class Entry<K,V> implements Map.Entry<K,V> {
// k,v值
K key;
V value;
// 树结构,左右子节点和父节点
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
// 节点颜色
boolean color = BLACK;
/**
* Make a new cell with given key, value, and parent, and with
* {@code null} child links, and BLACK color.
*/
Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}
/**
* Returns the key.
*
* @return the key
*/
public K getKey() {
return key;
}
/**
* Returns the value associated with the key.
*
* @return the value associated with the key
*/
public V getValue() {
return value;
}
/**
* Replaces the value currently associated with the key with the given
* value.
*
* @return the value associated with the key before this method was
* called
*/
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
// 同样对entry的相等语义进行了扩展
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());
}
// entry的hashcode是由key和value共同决定的,这会影响get和put的行为
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;
}
}
KeySet,Vaules,EntrySet实现
与HashMap相同,这三者本质上只是一个View,不会持有数据,Entry作为基本数据元素,被红黑树结构持有。红黑树以链表形式存在,其根节点被成员变量root持有。
class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator(getFirstEntry());
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
Object value = entry.getValue();
Entry<K,V> p = getEntry(entry.getKey());
// key相等和value相等即认为Entry相等
return p != null && valEquals(p.getValue(), value);
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>) o;
Object value = entry.getValue();
Entry<K,V> p = getEntry(entry.getKey());
if (p != null && valEquals(p.getValue(), value)) {
// 依赖于TreeMap的相关函数,后文分析
deleteEntry(p);
return true;
}
return false;
}
public int size() {
return TreeMap.this.size();
}
public void clear() {
TreeMap.this.clear();
}
public Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(TreeMap.this, null, null, 0, -1, 0);
}
}
static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
// m就表示当前TreeMap
private final NavigableMap<E, ?> m;
KeySet(NavigableMap<E,?> map) { m = map; }
// 串行迭代器
public Iterator<E> iterator() {
if (m instanceof TreeMap)
return ((TreeMap<E,?>)m).keyIterator();
else
return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
}
// 反序的串行迭代器,由DescendingKeyIterator支持
public Iterator<E> descendingIterator() {
if (m instanceof TreeMap)
return ((TreeMap<E,?>)m).descendingKeyIterator();
else
return ((TreeMap.NavigableSubMap<E,?>)m).descendingKeyIterator();
}
// 直接依赖于TreeMap的函数实现,内部类就是好啊,
// 如果不是后面需要通过代理或者装饰器模式对类进行增强,那么直接使用内部类将会非常方便。
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
public void clear() { m.clear(); }
public E lower(E e) { return m.lowerKey(e); }
public E floor(E e) { return m.floorKey(e); }
public E ceiling(E e) { return m.ceilingKey(e); }
public E higher(E e) { return m.higherKey(e); }
public E first() { return m.firstKey(); }
public E last() { return m.lastKey(); }
public Comparator<? super E> comparator() { return m.comparator(); }
public E pollFirst() {
Map.Entry<E,?> e = m.pollFirstEntry();
return (e == null) ? null : e.getKey();
}
public E pollLast() {
Map.Entry<E,?> e = m.pollLastEntry();
return (e == null) ? null : e.getKey();
}
public boolean remove(Object o) {
int oldSize = size();
m.remove(o);
return size() != oldSize;
}
public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
E toElement, boolean toInclusive) {
return new KeySet<>(m.subMap(fromElement, fromInclusive,
toElement, toInclusive));
}
public NavigableSet<E> headSet(E toElement, boolean inclusive) {
return new KeySet<>(m.headMap(toElement, inclusive));
}
public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
return new KeySet<>(m.tailMap(fromElement, inclusive));
}
public SortedSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
}
public SortedSet<E> headSet(E toElement) {
return headSet(toElement, false);
}
public SortedSet<E> tailSet(E fromElement) {
return tailSet(fromElement, true);
}
public NavigableSet<E> descendingSet() {
return new KeySet<>(m.descendingMap());
}
public Spliterator<E> spliterator() {
return keySpliteratorFor(m);
}
}
class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return new ValueIterator(getFirstEntry());
}
public int size() {
return TreeMap.this.size();
}
public boolean contains(Object o) {
return TreeMap.this.containsValue(o);
}
public boolean remove(Object o) {
for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e)) {
if (valEquals(e.getValue(), o)) {
deleteEntry(e);
return true;
}
}
return false;
}
public void clear() {
TreeMap.this.clear();
}
public Spliterator<V> spliterator() {
return new ValueSpliterator<>(TreeMap.this, null, null, 0, -1, 0);
}
}
串行迭代器
abstract class PrivateEntryIterator<T> implements Iterator<T> {
Entry<K,V> next;
Entry<K,V> lastReturned;
// 串行迭代器感知并行操作的关键,基于CAS思想
int expectedModCount;
PrivateEntryIterator(Entry<K,V> first) {
expectedModCount = modCount;
lastReturned = null;
next = first;
}
public final boolean hasNext() {
return next != null;
}
// 返回后一个元素,记住TreeMap是有序的,所以其前一个后一个元素是确定的
final Entry<K,V> nextEntry() {
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
next = successor(e);
lastReturned = e;
return e;
}
// 返回后一个元素
final Entry<K,V> prevEntry() {
Entry<K,V> e = next;
if (e == null)
throw new NoSuchElementException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
next = predecessor(e);
lastReturned = e;
return e;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
// deleted entries are replaced by their successors
if (lastReturned.left != null && lastReturned.right != null)
next = lastReturned;
deleteEntry(lastReturned);
expectedModCount = modCount;
lastReturned = null;
}
}
// 下面就是具体的串行迭代器实现,都是基于Privateiterator实现
final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> {
EntryIterator(Entry<K,V> first) {
super(first);
}
public Map.Entry<K,V> next() {
return nextEntry();
}
}
final class ValueIterator extends PrivateEntryIterator<V> {
ValueIterator(Entry<K,V> first) {
super(first);
}
public V next() {
return nextEntry().value;
}
}
final class KeyIterator extends PrivateEntryIterator<K> {
KeyIterator(Entry<K,V> first) {
super(first);
}
public K next() {
return nextEntry().key;
}
}
final class DescendingKeyIterator extends PrivateEntryIterator<K> {
DescendingKeyIterator(Entry<K,V> first) {
super(first);
}
// 反序嘛,下一个就是前一个
public K next() {
return prevEntry().key;
}
public void remove() {
if (lastReturned == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
deleteEntry(lastReturned);
lastReturned = null;
expectedModCount = modCount;
}
}
并行迭代器
static class TreeMapSpliterator<K,V> {
// 当前TreeMap
final TreeMap<K,V> tree;
// 以下成员变量是为了支持TrySplit而存在的,在HashMap中有数组和链表支持,所以可以直接二分
// 但是在TreeMap中,只有树结构,所以TrySplit采取的划分规则为从根节点开始(每次划分后形成的也是树),
// 左子树单独成为一个TreeMapSpliterator,根放在右子树中,这是API规定的,右边的元素大于等于左边的元素
// 默认null,但是在第一次使用的时候会被初始化为第一个元素,它依赖于est的值。
TreeMap.Entry<K,V> current; // traverser; initially first node in range
// 默认null
TreeMap.Entry<K,V> fence; // one past last, or null
// 默认0
int side; // 0: top, -1: is a left split, +1: right
// 默认-1,当它小于0的时候,需要对current初始化和获取tree的size
int est; // size estimate (exact only for top-level)
int expectedModCount; // for CME checks
TreeMapSpliterator(TreeMap<K,V> tree,
TreeMap.Entry<K,V> origin, TreeMap.Entry<K,V> fence,
int side, int est, int expectedModCount) {
this.tree = tree;
this.current = origin;
this.fence = fence;
this.side = side;
this.est = est;
this.expectedModCount = expectedModCount;
}
final int getEstimate() { // force initialization
int s; TreeMap<K,V> t;
if ((s = est) < 0) {
if ((t = tree) != null) {
current = (s == -1) ? t.getFirstEntry() : t.getLastEntry();
s = est = t.size;
expectedModCount = t.modCount;
}
else
s = est = 0;
}
return s;
}
public final long estimateSize() {
return (long)getEstimate();
}
}
static final class KeySpliterator<K,V>
extends TreeMapSpliterator<K,V>
implements Spliterator<K> {
KeySpliterator(TreeMap<K,V> tree,
TreeMap.Entry<K,V> origin, TreeMap.Entry<K,V> fence,
int side, int est, int expectedModCount) {
super(tree, origin, fence, side, est, expectedModCount);
}
// 来看看在TreeMap中的划分规则,简单来说划分规则就是不断断开左子树的过程,
// 断开后对于左子树来说,起点(current)就是左子树根节点,终点就是原树的根节点(fence)(不包括)
// 断开后对于右子树来说,起点就是原子树的根节点,终点为null(fence),表示一直遍历到最后为止
// 之所以断左子树就可以保证是在有序集合上的二分是由红黑树的性质保证的,红黑树本质上
// 是一棵二叉排序树,左子树<根节点<右子树
public KeySpliterator<K,V> trySplit() {
if (est < 0)
// 这个函数会返回当前spliterator中的元素个数
getEstimate(); // force initialization
// side会影响spiterator的特征
int d = side;
// 下面在确定二分后左子树的右边界,e表示当前树的起点,fence表示终点,d表示左子树还是右子树
TreeMap.Entry<K,V> e = current, f = fence,
// 如果当前节点为空,或者当前节点就是重点,则表示该子树已经没有元素了,不可再进行分割
// 如果还有元素可以进行分割,则若为左子树,则新的终点为原来终点的左节点
// 如果为右子树,起点为原子树的根,终点为当前节点的左子树
s = ((e == null || e == f) ? null : // empty
(d == 0) ? tree.root : // was top
(d > 0) ? e.right : // was right
(d < 0 && f != null) ? f.left : // was left
null);
// 如果终点存在且终点没有和起点重合,即还可以再分割
if (s != null && s != e && s != f &&
tree.compare(e.key, s.key) < 0) { // e not already past s
side = 1;
return new KeySpliterator<>
(tree, e, current = s, -1, est >>>= 1, expectedModCount);
}
// 不能分割则返回null,在tree容量有限的情况下,有限次调用后返回结果终将为null
return null;
}
// 对剩余每一个元素执行action
public void forEachRemaining(Consumer<? super K> action) {
if (action == null)
throw new NullPointerException();
if (est < 0)
getEstimate(); // force initialization
TreeMap.Entry<K,V> f = fence, e, p, pl;
if ((e = current) != null && e != f) {
current = f; // exhaust
do {
action.accept(e.key);
if ((p = e.right) != null) {
while ((pl = p.left) != null)
p = pl;
}
else {
while ((p = e.parent) != null && e == p.right)
e = p;
}
} while ((e = p) != null && e != f);
if (tree.modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
// 对下一个元素执行action
public boolean tryAdvance(Consumer<? super K> action) {
TreeMap.Entry<K,V> e;
if (action == null)
throw new NullPointerException();
if (est < 0)
getEstimate(); // force initialization
if ((e = current) == null || e == fence)
return false;
current = successor(e);
action.accept(e.key);
if (tree.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
public int characteristics() {
return (side == 0 ? Spliterator.SIZED : 0) |
Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
}
public final Comparator<? super K> getComparator() {
return tree.comparator;
}
}
// ValueSpliterator,EntrySpliterator,DescendingKeySpliterator与KeySpliterator相似,不贴代码了
关于红黑树的二分下面给出一个例子:
NavigableMap<Integer,String> map = new TreeMap<>();
map.put(4, "k");
map.put(2, "k");
map.put(6, "k");
map.put(1, "k");
map.put(3, "k");
map.put(5, "k");
map.put(7, "k");
Spliterator<Map.Entry<Integer, String>> entrySpliterator1 = entries.spliterator();
// 第一次进行划分
Spliterator<Map.Entry<Integer, String>> entrySpliterator2 = entrySpliterator1.trySplit();
// 对右子树再进行一次划分
Spliterator<Map.Entry<Integer, String>> entrySpliterator3 = entrySpliterator1.trySplit();
// 查看结果
System.out.println("The elements of entrySpliterator1:");
entrySpliterator1.forEachRemaining(e -> System.out.println(e.getKey()));
System.out.println("The elements of entrySpliterator2:");
entrySpliterator2.forEachRemaining(e -> System.out.println(e.getKey()));
System.out.println("The elements of entrySpliterator3:");
entrySpliterator3.forEachRemaining(e -> System.out.println(e.getKey()));
它构建了下面这样一棵红黑树
当第一次进行划分后,左右子树及程序中关键变量值如下
对entrySpliterator1再进行一次划分:
将各个迭代器结果打印出来结果如下:
SubMap
这部分没有太难的东西,请各位自行阅读源码
TreeMap中重要的方法
get方法
public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
// 同样是二分查找,只是比较的规则由用户指定的comparator决定
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
Entry<K,V> p = root;
// 红黑树的二分查找
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
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;
// split comparator and comparable paths
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();
@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);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
// 插入树后自平衡过程
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
下面重点介绍fixAfterInsertion(e)自平衡函数,插入的自平衡规则较为简单,面试的话基本上到这一步就可以了。先给出自平衡规则的思维导图:
图中的变换规则是指:把父节点设置为黑色,把叔叔节点也设置为黑色,把祖父节点设置为红色,将当前节点设置为祖父节点,对当前节点进行冲突分析,也就是迭代过程。实现代码如下:
private void fixAfterInsertion(Entry<K,V> x) {
// x表示插入节点,插入节点默认为红色
x.color = RED;
// 处理父节点为红色的情况,注意是循环,也就是说自平衡不是一次就可以完成,是一个需要迭代的过程
while (x != null && x != root && x.parent.color == RED) {
// 如果父节点是左子树
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
// y表示叔叔节点
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;
}
remove方法
对于这个方法,我也不举例子了,我也搞不懂其原理是什么,只是把代码实现的逻辑整理了出来,至于为什么这么写它就可以保证平衡,我也搞不明白。
删除一个元素首先需要在树中定位元素,这部分通过调用get实现,此时我们称该节点为被定位节点。第二步是确定需要被删除的节点,需要说明的是被删除的元素和被删除的节点并不相同,红黑树是二叉平衡树的扩展,所以其删除方式与二叉平衡树树相似。具体的来说,如果被定位节点是叶子节点,则该节点就是需要被删除的节点,如果被定位节点有一个子节点,则该子节点就是需要被删除的节点,如果被定位节点有两个子节点,则被删除节点为被定位节点的直接前继或者直接后驱。然后将被删除节点的值与被定位节点的值进行交换,然后将被删除节点删除。此时,被删除的节点只能是叶子节点。删除节点后,需要对树进行自平衡,自平衡的相关逻辑如下:
代码实现如下:
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
// If strictly internal, copy successor's element to p and then make p
// point to successor.
// 下面再确定需要被删除的节点
// 如果左右节点都不为空
if (p.left != null && p.right != null) {
// successor函数获取直接后继
Entry<K,V> s = successor(p);
// 交换两个节点的元素
p.key = s.key;
p.value = s.value;
p = s;
} // p has 2 children
// Start fixup at replacement node, if it exists.
// 单子树情况
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
// 删除节点
if (replacement != null) {
// Link replacement to parent
replacement.parent = p.parent;
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
// Null out links so they are OK to use by fixAfterDeletion.
p.left = p.right = p.parent = null;
// Fix replacement
// 删除之后自平衡
if (p.color == BLACK)
fixAfterDeletion(replacement);
}
// 删除节点是根节点的情况
else if (p.parent == null) { // return if we are the only node.
root = null;
}
// 没有子节点
else { // No children. Use self as phantom replacement and unlink.
// 删除黑色节点需要自平衡
if (p.color == BLACK)
fixAfterDeletion(p);
// 删除节点
if (p.parent != null) {
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
p.parent = null;
}
}
}
下面来看以下自平衡函数,所有的逻辑都在上面的导图中。
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);
// 待处理节点为父节点,之后返回while循环,进行下一次自平衡过程
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));
// 设置待处理节点为root,这样可以结束循环
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;
}
}
}
// 到这里只有一种情况,即x为root,将root设置为黑色,这是红黑树性质的要求。
setColor(x, BLACK);
}
这里关于NavigableMap和SubMap的相关API没有介绍,主要是其实现都相对简单,可以自己看看源码。另外提一下,对比二叉平衡树简单理解一下红黑树,二叉平衡树要求所有非叶节点的左右子树高度差绝对值小于1,在插入元素进行自平衡过程中,最差的情况是需要逐层调整直到根节点,而且需要多次调整的情况十分频繁,造成二叉平衡树的插入性能十分的差,但是相对来说,在基于排序树的查找结构中,二叉平衡树的查找效率是很高的。为了平衡插入和查找的性能,红黑树适当放宽了左右子树高度差的范围,比如子树的高度只由黑色节点决定,所以在红黑树中,如果我们只看黑色节点,那么由黑色节点组成的树是一棵很完美的二叉平衡树,其每个节点的相对高度都为0。但是为了削弱二叉平衡树的要求,在黑色节点之间允许插入红色节点,并且规定插入红色节点不会影响树的高度,这时,实际上每棵子树的高度便有了动态变化的可能,但是为了将这种动态变化限制在一定的程度上,要求不能出现两个连续的红色节点,此时,红色节点出现的数量便有了限制,即在红黑节点交替出现的情况下,红色节点达到了最大值,此时,左右子树高度的绝对差便在0到黑色节点数量之间波动,正是由于扩大了高度差的空间,以前在平衡二叉树下需要调整的情况在红黑树的情况下便不再需要调整了,所以提高了插入效率,同时整棵红黑树的查找效率由黑色节点兜底,于是,在没有极大损耗查找效率的情况下,极大的提高了插入效率,这就是红黑树的优点。当然,这都只是定向分析,至于定量分析,网上有很多相关数据。