需要先了解红黑树,这是之前分析红黑树的文章。
之前在分析红黑树时,我认为红黑树=二叉查找树+红黑平衡,关于二叉查找树这是递归版本的,而在TreeMap中实现的是非递归版本的。
TreeMap的继承关系:
关于SortedMap,NavigableMap
get与put操作都很简单,注意TreeMap不允许键为null,对TreeMap来说comparator不为null则利用该比较器进行key键比较,否则利用key键本身这要求键必须实现Comparable接口,实现自己的compareTo逻辑,所以对于TreeMap作为键的对象要么你提供一个comparator比较器,要么这个对象是Comparable。
对于HashMap,它允许一个键为null的节点,因为HashMap比较的是hash值(null的hash值在HashMap中为0),但作为键的对象最好重写equals方法,当然重写equals就应该重写hashcode方法。
1.8后Comparator接口更加强健,配合lambda,代码更加简洁易懂。
remove
public V remove(Object key) {
Entry p = getEntry(key);
if (p == null)
return null;
V oldValue = p.value;
deleteEntry(p);
return oldValue;
}
private void deleteEntry(Entry 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) {
Entry 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 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;
}
}
}
若当前节点的左右皆不为空,则找到其右子节点的最左节点与其进行键值的替换,问题就转化为删除这个最小节点。所以最后要删除的节点存在两种情况:1,没有子节点。2,只有一个子孩子非空。对于这两种情况的处理不同。情况1,若它颜色为黑则先进行红黑调整fixAfterDeletion,后在根据情况进行删除。该方法介绍在红黑树。情况2,先删除该节点,若其为黑,对其左/右子树进行红黑平衡。
successor
static TreeMap.Entry successor(Entry t) {
if (t == null)
return null;
else if (t.right != null) {
Entry p = t.right;
while (p.left != null)
p = p.left;
return p;
} else {
Entry p = t.parent;
Entry ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}
返回的是最接近的大于t的节点,就是大于t的节点中最小的那个节点。
两种情况,右子孩子非空则返回其最左节点;为空则沿着右连接往上直到拐点。
图中t, E是父子关系。
按照上面的代码返回的就是的就是P节点,对于P若它有右子树那么也只可能有一个节点,且为红,这是由红黑树性质决定的。P是大于t节点中是最接近于t的,你可以在该图中任意延申扩展,在根据大小关系推,便可得到证明。
图中P,M是父子关系。
P节点是大于 t 中最小的节点,也就是最接近的节点。
predecessor(t)
该方法返回的是小于 t 的节点中最大的节点,也就是最接近的节点。
static Entry predecessor(Entry t) {
if (t == null)
return null;
else if (t.left != null) {
Entry p = t.left;
while (p.right != null)
p = p.right;
return p;
} else {
Entry p = t.parent;
Entry ch = t;
while (p != null && ch == p.left) {
ch = p;
p = p.parent;
}
return p;
}
}
像上面一样画出图就能够看出。
之前在SortedMap,NavigableMap中介绍了很多返回各种视图的方法,现在来看看在TreeMap中的实现。
lowerEntry
返回最接近的小于key的Entry
public Map.Entry lowerEntry(K key) {
return exportEntry(getLowerEntry(key));
}
static Map.Entry exportEntry(TreeMap.Entry e) {
return (e == null) ? null :
new AbstractMap.SimpleImmutableEntry<>(e);
}
final Entry getLowerEntry(K key) {
Entry p = root;
while (p != null) {
int cmp = compare(key, p.key);
if (cmp > 0) {
if (p.right != null)
p = p.right;
else
return p;
} else {
if (p.left != null) {
p = p.left;
} else {
Entry parent = p.parent;
Entry ch = p;
while (parent != null && ch == parent.left) {
ch = parent;
parent = parent.parent;
}
return parent;
}
}
}
return null;
}
1,getLowerEntry
下面所说的左链路:上面的节点一定大于key。右链路:上面的节点一定小于key。所以每条链路并不包括末尾的拐点,它是下一条链路的开始节点。
图中代表的是一段寻找的路径,同中K,P和A,B是父子关系。按照代码可知A节点就是最接近key的节点,为什么?所有左链接上的节点都是大于key的,那么最接近的一定是那个右路径上最大的节点,A就是,比如A与E比较,
T > E > key;A > T。所以A > E,任意取右链路上的节点都可得到此结论,由此可推断出A是路径中所有右链路中最大的节点,也就是最接近key的节点。
上面证明了路径中最后一条右链路的末尾节点(不是指拐点,如B,它是左链路开头节点)一定是最接近key的节点。但是,还有一个疑问,除了这条链路的其它树节点就一定不符合吗?是的,它们一定不符合,从上往下的寻找中往左拐说明该拐点大于key,向左寻找小于key的节点,找到后向右拐说明该拐点小于key,向其右侧寻找介于该拐点与key之间最大的值,如上图中K与P节点,K < key,此时K的左儿子P,P < K所以P一定不是最接近的。所以要找到null为止,再根据null是左儿子或右儿子的情况来做相应处理,处理就是找到最后的右链路的最后节点。
所以当p.right == null 时,p就是那个最接近的节点,直接返回。
2,exportEntry
static Map.Entry exportEntry(TreeMap.Entry e) {
return (e == null) ? null :
new AbstractMap.SimpleImmutableEntry<>(e);
}
getLowerEntry返回那个最接近key的节点,获取其key与value构造一个SimpleImmutableEntry对象,然后返回。SimpleImmutableEntry提供一些基本方法如getKey,getValue,equals,hashcode,toString,它的setValue抛出UnsupportedOperationException异常,也就是不可改变
其它方法如:lowerKey,floorEntry,floorKey代码逻辑与lowerEntry相同。
higherEntry,higherKey,ceilingEntry,ceilingKey处理方法与lowerEntry相反,它们找的是路径中最后的左链路的最后节点。
下面来分析一个内部类NavigableSubMap,它与之后要分析的很多方法有关。
NavigableSubMap
只对原TreeMap的一部分即子map进行操作,对这部分的可以put,get,remove,ceilingEntry,higherKey....就像一个新的TreeMap,但并非如此,你的操作被限定在一定范围内,并且是直接影响到原map的,本质上就是在对原map树的相应节点进行操作,这就是NavigableSubMap实现的功能,也就是塑造一个特定范围的原map的视图,仍然是同一map,操作互相影响。
所以你在下面的代码会看到NavigableSubMap里的put,get,remove.....等方法底层都是调用TreeMap的相应方法来实现,只不过在开始加了范围的判断。
abstract static class NavigableSubMap extends AbstractMap
implements NavigableMap, java.io.Serializable {
private static final long serialVersionUID = -2102997345730753016L;
final TreeMap m;
通过这6个变量来控制起始与结束边界。
(fromStart,lo,loInclusive)代表起点,若fromStart为true则为map的最左节点,即最小。
若fromStart为false,lo为起点,是否包含lo由loInclusive决定。
(toEnd, hi, hiInclusive)代表终点,规则与上面一样。
final K lo, hi;
final boolean fromStart, toEnd;
final boolean loInclusive, hiInclusive;
NavigableSubMap(TreeMap m,
boolean fromStart, K lo, boolean loInclusive,
boolean toEnd, K hi, boolean hiInclusive) {
...................
}
一些判断边界的方法:tooLow,tooHigh,inRange(Object key),inClosedRange,inRange(Object key, boolean inclusive),代码就不贴了。
另一类方法,如absLowest,返回范围内最小节点
final TreeMap.Entry absLowest() {
TreeMap.Entry e =
(fromStart ? m.getFirstEntry() :
(loInclusive ? m.getCeilingEntry(lo) :
m.getHigherEntry(lo)));
return (e == null || tooHigh(e.key)) ? null : e;
}
方法逻辑很清晰,利用的方法的逻辑在上面解析过。
类似的方法还有:
absHighest范围内最大节点。
absCeiling(K key)范围内最接近的大于等于key的节点,为null说明超过上界。
absHigher(K key)范围内最接近的大于key的节点。
absFloor(K key)范围内最接近的小于等于key的节点
absLower(K key)范围内最接近的小于key的节点
absHighFence返回最接近的大于范围内最大值的节点
absLowFence返回最接近的小于范围内最小值的节点
还有一些让子类去实现发抽象方法
abstract TreeMap.Entry subLowest();
abstract TreeMap.Entry subHighest();
abstract TreeMap.Entry subCeiling(K key);
abstract TreeMap.Entry subHigher(K key);
abstract TreeMap.Entry subFloor(K key);
abstract TreeMap.Entry subLower(K key);
升序
abstract Iterator keyIterator();
abstract Spliterator keySpliterator();
降序
abstract Iterator descendingKeyIterator();
接下来是由NavigableSubMap重新实现的操作方法
NavigableSubMap是个abstract类,AbstractMap的entrySet方法它并没有实现,
而是交给了子类去实现
public boolean isEmpty() {
return (fromStart && toEnd) ? m.isEmpty() : entrySet().isEmpty();
}
public int size() {
return (fromStart && toEnd) ? m.size() : entrySet().size();
}
public final boolean containsKey(Object key) {
return inRange(key) && m.containsKey(key);
}
public final V put(K key, V value) {
if (!inRange(key))
throw new IllegalArgumentException("key out of range");
return m.put(key, value);
}
public final V get(Object key) {
return !inRange(key) ? null : m.get(key);
}
public final V remove(Object key) {
return !inRange(key) ? null : m.remove(key);
}
剩下的这些方法调用的各种subxxx()方法,都是要由子类去实现的
public final Map.Entry ceilingEntry(K key) {
return exportEntry(subCeiling(key));
}
public final K ceilingKey(K key) {
return keyOrNull(subCeiling(key));
}
public final Map.Entry higherEntry(K key) {
return exportEntry(subHigher(key));
}
public final K higherKey(K key) {
return keyOrNull(subHigher(key));
}
public final Map.Entry floorEntry(K key) {
return exportEntry(subFloor(key));
}
public final K floorKey(K key) {
return keyOrNull(subFloor(key));
}
public final Map.Entry lowerEntry(K key) {
return exportEntry(subLower(key));
}
public final K lowerKey(K key) {
return keyOrNull(subLower(key));
}
public final K firstKey() {
return key(subLowest());
}
public final K lastKey() {
return key(subHighest());
}
public final Map.Entry firstEntry() {
return exportEntry(subLowest());
}
public final Map.Entry lastEntry() {
return exportEntry(subHighest());
}
public final Map.Entry pollFirstEntry() {
TreeMap.Entry e = subLowest();
Map.Entry result = exportEntry(e);
if (e != null)
m.deleteEntry(e);
return result;
}
public final Map.Entry pollLastEntry() {
TreeMap.Entry e = subHighest();
Map.Entry result = exportEntry(e);
if (e != null)
m.deleteEntry(e);
return result;
}
接下来介绍各种视图方法,
降序视图
transient NavigableMap descendingMapView;
Entry视图
transient EntrySetView entrySetView;
key视图
transient KeySet navigableKeySetView;
public final NavigableSet navigableKeySet() {
KeySet nksv = navigableKeySetView;
return (nksv != null) ? nksv :
(navigableKeySetView = new TreeMap.KeySet<>(this));
}
public final Set keySet() {
return navigableKeySet();
}
这里descendingMap()是NavigableMap接口的方法,NavigableSubMap并没有实现它,
交由子类去实现
public NavigableSet descendingKeySet() {
return descendingMap().navigableKeySet();
}
subMap()同上,实现在子类
public final SortedMap subMap(K fromKey, K toKey) {
return subMap(fromKey, true, toKey, false);
}
headMap同上,实现在子类
public final SortedMap headMap(K toKey) {
return headMap(toKey, false);
}
tailMap同上,实现在子类
public final SortedMap tailMap(K fromKey) {
return tailMap(fromKey, true);
}
来看看各个视图的类
EntrySetView
abstract class EntrySetView extends AbstractSet> {
private transient int size = -1, sizeModCount;
public int size() {
if (fromStart && toEnd)
return m.size();
if (size == -1 || sizeModCount != m.modCount) {
sizeModCount = m.modCount;
size = 0;
Iterator> i = iterator();
while (i.hasNext()) {
size++;
i.next();
}
}
return size;
}
public boolean isEmpty() {
TreeMap.Entry n = absLowest();
return n == null || tooHigh(n.key);
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> entry = (Map.Entry,?>) o;
Object key = entry.getKey();
if (!inRange(key))
return false;
TreeMap.Entry,?> node = m.getEntry(key);
return node != null &&
valEquals(node.getValue(), entry.getValue());
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> entry = (Map.Entry,?>) o;
Object key = entry.getKey();
if (!inRange(key))
return false;
TreeMap.Entry node = m.getEntry(key);
if (node!=null && valEquals(node.getValue(),
entry.getValue())) {
m.deleteEntry(node);
return true;
}
return false;
}
}
迭代器SubMapIterator
abstract class SubMapIterator implements Iterator {
TreeMap.Entry lastReturned; 用于记录本次返回的接待你
TreeMap.Entry next; 指向下次要返回的节点
在向前或向后遍历时需要知道上下边界在哪,达到该边界就代表超出范围
final Object fenceKey;
int expectedModCount;
SubMapIterator(TreeMap.Entry first,
TreeMap.Entry fence) {
expectedModCount = m.modCount;
lastReturned = null;
next = first;
fenceKey = fence == null ? UNBOUNDED : fence.key;
}
public final boolean hasNext() {
return next != null && next.key != fenceKey;
}
上面说过successor返回的是大于e节点中的最小的那个节点,所以顺序性得到保证。
final TreeMap.Entry nextEntry() {
TreeMap.Entry e = next;
if (e == null || e.key == fenceKey)
throw new NoSuchElementException();
if (m.modCount != expectedModCount)
throw new ConcurrentModificationException();
next = successor(e);
lastReturned = e;
return e;
}
predecessor返回的是小于e节点中的最大的那个节点,遍历得到的结果是从大到小的顺序。
final TreeMap.Entry prevEntry() {
TreeMap.Entry e = next;
if (e == null || e.key == fenceKey)
throw new NoSuchElementException();
if (m.modCount != expectedModCount)
throw new ConcurrentModificationException();
next = predecessor(e);
lastReturned = e;
return e;
}
该方法是与nextEntry配合的,不能和preEntry混用
final void removeAscending() {
if (lastReturned == null)
throw new IllegalStateException();
if (m.modCount != expectedModCount)
throw new ConcurrentModificationException();
在遍历过程中得到了一个节点e,现在要删除它,此时lastReturned同样指向e,
若其左右子树皆不为null,则在deleteEntry删除过程中实际删除的是successor返回的节点,
而它正是next指针在此时指向的节点,所以需要下面这步。
if (lastReturned.left != null && lastReturned.right != null)
next = lastReturned;
m.deleteEntry(lastReturned);
lastReturned = null;
expectedModCount = m.modCount;
}
该方法是与preEntry配合使用
final void removeDescending() {
if (lastReturned == null)
throw new IllegalStateException();
if (m.modCount != expectedModCount)
throw new ConcurrentModificationException();
m.deleteEntry(lastReturned);
lastReturned = null;
expectedModCount = m.modCount;
}
}
各种继承SubMapIterator实现的不同迭代器
Entry的升序迭代器
final class SubMapEntryIterator extends SubMapIterator> {
SubMapEntryIterator(TreeMap.Entry first,
TreeMap.Entry fence) {
super(first, fence);
}
public Map.Entry next() {
return nextEntry();
}
public void remove() {
removeAscending();
}
}
Entry的降序迭代器
final class DescendingSubMapEntryIterator extends SubMapIterator> {
DescendingSubMapEntryIterator(TreeMap.Entry last,
TreeMap.Entry fence) {
super(last, fence);
}
public Map.Entry next() {
return prevEntry();
}
public void remove() {
removeDescending();
}
}
键的升序迭代器
final class SubMapKeyIterator extends SubMapIterator
implements Spliterator {
SubMapKeyIterator(TreeMap.Entry first,
TreeMap.Entry fence) {
...............
}
键的降序迭代器
final class DescendingSubMapKeyIterator extends SubMapIterator
implements Spliterator {
DescendingSubMapKeyIterator(TreeMap.Entry last,
TreeMap.Entry fence) {
.....................
}
接下来看看NavigableSubMap的用途:
subMap
【fromKey, toKey)左闭右开,返回该区间范围内的原map的视图
public SortedMap subMap(K fromKey, K toKey) {
return subMap(fromKey, true, toKey, false);
}
public NavigableMap subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive) {
return new AscendingSubMap<>(this,
false, fromKey, fromInclusive,
false, toKey, toInclusive);
}
AscendingSubMap
static final class AscendingSubMap extends NavigableSubMap {
private static final long serialVersionUID = 912986545866124060L;
AscendingSubMap(TreeMap m,
boolean fromStart, K lo, boolean loInclusive,
boolean toEnd, K hi, boolean hiInclusive) {
super(m, fromStart, lo, loInclusive, toEnd, hi, hiInclusive);
}
public Comparator super K> comparator() {
return m.comparator();
}
public NavigableMap subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive) {
if (!inRange(fromKey, fromInclusive))
throw new IllegalArgumentException("fromKey out of range");
if (!inRange(toKey, toInclusive))
throw new IllegalArgumentException("toKey out of range");
return new AscendingSubMap<>(m,
false, fromKey, fromInclusive,
false, toKey, toInclusive);
}
public NavigableMap headMap(K toKey, boolean inclusive) {
if (!inRange(toKey, inclusive))
throw new IllegalArgumentException("toKey out of range");
return new AscendingSubMap<>(m,
fromStart, lo, loInclusive,
false, toKey, inclusive);
}
public NavigableMap tailMap(K fromKey, boolean inclusive) {
if (!inRange(fromKey, inclusive))
throw new IllegalArgumentException("fromKey out of range");
return new AscendingSubMap<>(m,
false, fromKey, inclusive,
toEnd, hi, hiInclusive);
}
public NavigableMap descendingMap() {
NavigableMap mv = descendingMapView;
return (mv != null) ? mv :
(descendingMapView =
new DescendingSubMap<>(m,
fromStart, lo, loInclusive,
toEnd, hi, hiInclusive));
}
Iterator keyIterator() {
return new SubMapKeyIterator(absLowest(), absHighFence());
}
Spliterator keySpliterator() {
return new SubMapKeyIterator(absLowest(), absHighFence());
}
Iterator descendingKeyIterator() {
return new DescendingSubMapKeyIterator(absHighest(), absLowFence());
}
final class AscendingEntrySetView extends EntrySetView {
public Iterator> iterator() {
return new SubMapEntryIterator(absLowest(), absHighFence());
}
}
public Set> entrySet() {
EntrySetView es = entrySetView;
return (es != null) ? es : (entrySetView = new AscendingEntrySetView());
}
TreeMap.Entry subLowest() { return absLowest(); }
TreeMap.Entry subHighest() { return absHighest(); }
TreeMap.Entry subCeiling(K key) { return absCeiling(key); }
TreeMap.Entry subHigher(K key) { return absHigher(key); }
TreeMap.Entry subFloor(K key) { return absFloor(key); }
TreeMap.Entry subLower(K key) { return absLower(key); }
}
代码设计时要低耦合高复用,NavigableSubMap就充分实现了这一准则。比如当你调用subMap得到了一个SortedMap对象,调用isEmpty()方法,Debug看看这一过程,它在多个方法与内部类中跳转。
类似用AscendingSubMap来实现视图的方法有:
大于fromKey的视图,inclusive控制是否包括fromKey
public NavigableMap tailMap(K fromKey, boolean inclusive) {
return new AscendingSubMap<>(this,
false, fromKey, inclusive,
true, null, true);
}
小于toKey的视图
public NavigableMap headMap(K toKey, boolean inclusive) {
return new AscendingSubMap<>(this,
true, null, true,
false, toKey, inclusive);
}
descendingMap()
public NavigableMap descendingMap() {
NavigableMap km = descendingMap;
return (km != null) ? km :
(descendingMap = new DescendingSubMap<>(this,
true, null, true,
true, null, true));
}
DescendingSubMap
AscendingSubMap是升序的子map视图,而DescendingSubMap与其相反,它是从上边界往下边界的顺序,这就是二者的不同,除此之外它们的实现思路相同。
TreeMap还有很多的内部类,即相关操作,之后再分析.........