需要先了解紅黑樹,這是之前分析紅黑樹的文章。
之前在分析紅黑樹時,我認為紅黑樹=二叉查找樹+紅黑平衡,關于二叉查找樹這是遞歸版本的,而在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<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 strictly internal, copy successor's element to p and then make p
// point to successor.
if (p.left != null && p.right != null) {
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;
}
}
}
若當前節點的左右皆不為空,則找到其右子節點的最左節點與其進行鍵值的替換,問題就轉化為刪除這個最小節點。所以最后要刪除的節點存在兩種情況:1,沒有子節點。2,只有一個子孩子非空。對于這兩種情況的處理不同。情況1,若它顏色為黑則先進行紅黑調整fixAfterDeletion,后在根據情況進行刪除。該方法介紹在紅黑樹。情況2,先刪除該節點,若其為黑,對其左/右子樹進行紅黑平衡。
successor
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;
}
}
返回的是最接近的大于t的節點,就是大于t的節點中最小的那個節點。
兩種情況,右子孩子非空則返回其最左節點;為空則沿著右連接往上直到拐點。
圖中t, E是父子關系。
按照上面的代碼返回的就是的就是P節點,對于P若它有右子樹那么也只可能有一個節點,且為紅,這是由紅黑樹性質決定的。P是大于t節點中是最接近于t的,你可以在該圖中任意延申擴展,在根據大小關系推,便可得到證明。
圖中P,M是父子關系。
P節點是大于 t 中最小的節點,也就是最接近的節點。
predecessor(t)
該方法返回的是小于 t 的節點中最大的節點,也就是最接近的節點。
static <K,V> Entry<K,V> predecessor(Entry<K,V> t) {
if (t == null)
return null;
else if (t.left != null) {
Entry<K,V> p = t.left;
while (p.right != null)
p = p.right;
return p;
} else {
Entry<K,V> p = t.parent;
Entry<K,V> ch = t;
while (p != null && ch == p.left) {
ch = p;
p = p.parent;
}
return p;
}
}
像上面一樣畫出圖就能夠看出。
之前在SortedMap,NavigableMap中介紹了很多返回各種視圖的方法,現在來看看在TreeMap中的實現。
lowerEntry
返回最接近的小于key的Entry
public Map.Entry<K,V> lowerEntry(K key) {
return exportEntry(getLowerEntry(key));
}
static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) {
return (e == null) ? null :
new AbstractMap.SimpleImmutableEntry<>(e);
}
final Entry<K,V> getLowerEntry(K key) {
Entry<K,V> 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<K,V> parent = p.parent;
Entry<K,V> 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 <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> 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<K,V> extends AbstractMap<K,V>
implements NavigableMap<K,V>, java.io.Serializable {
private static final long serialVersionUID = -2102997345730753016L;
final TreeMap<K,V> 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<K,V> 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<K,V> absLowest() {
TreeMap.Entry<K,V> 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<K,V> subLowest();
abstract TreeMap.Entry<K,V> subHighest();
abstract TreeMap.Entry<K,V> subCeiling(K key);
abstract TreeMap.Entry<K,V> subHigher(K key);
abstract TreeMap.Entry<K,V> subFloor(K key);
abstract TreeMap.Entry<K,V> subLower(K key);
升序
abstract Iterator<K> keyIterator();
abstract Spliterator<K> keySpliterator();
降序
abstract Iterator<K> 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<K,V> ceilingEntry(K key) {
return exportEntry(subCeiling(key));
}
public final K ceilingKey(K key) {
return keyOrNull(subCeiling(key));
}
public final Map.Entry<K,V> higherEntry(K key) {
return exportEntry(subHigher(key));
}
public final K higherKey(K key) {
return keyOrNull(subHigher(key));
}
public final Map.Entry<K,V> floorEntry(K key) {
return exportEntry(subFloor(key));
}
public final K floorKey(K key) {
return keyOrNull(subFloor(key));
}
public final Map.Entry<K,V> 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<K,V> firstEntry() {
return exportEntry(subLowest());
}
public final Map.Entry<K,V> lastEntry() {
return exportEntry(subHighest());
}
public final Map.Entry<K,V> pollFirstEntry() {
TreeMap.Entry<K,V> e = subLowest();
Map.Entry<K,V> result = exportEntry(e);
if (e != null)
m.deleteEntry(e);
return result;
}
public final Map.Entry<K,V> pollLastEntry() {
TreeMap.Entry<K,V> e = subHighest();
Map.Entry<K,V> result = exportEntry(e);
if (e != null)
m.deleteEntry(e);
return result;
}
接下來介紹各種視圖方法,
降序視圖
transient NavigableMap<K,V> descendingMapView;
Entry視圖
transient EntrySetView entrySetView;
key視圖
transient KeySet<K> navigableKeySetView;
public final NavigableSet<K> navigableKeySet() {
KeySet<K> nksv = navigableKeySetView;
return (nksv != null) ? nksv :
(navigableKeySetView = new TreeMap.KeySet<>(this));
}
public final Set<K> keySet() {
return navigableKeySet();
}
這里descendingMap()是NavigableMap接口的方法,NavigableSubMap并沒有實現它,
交由子類去實現
public NavigableSet<K> descendingKeySet() {
return descendingMap().navigableKeySet();
}
subMap()同上,實現在子類
public final SortedMap<K,V> subMap(K fromKey, K toKey) {
return subMap(fromKey, true, toKey, false);
}
headMap同上,實現在子類
public final SortedMap<K,V> headMap(K toKey) {
return headMap(toKey, false);
}
tailMap同上,實現在子類
public final SortedMap<K,V> tailMap(K fromKey) {
return tailMap(fromKey, true);
}
來看看各個視圖的類
EntrySetView
abstract class EntrySetView extends AbstractSet<Map.Entry<K,V>> {
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<K,V> 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<K,V> node = m.getEntry(key);
if (node!=null && valEquals(node.getValue(),
entry.getValue())) {
m.deleteEntry(node);
return true;
}
return false;
}
}
迭代器SubMapIterator
abstract class SubMapIterator<T> implements Iterator<T> {
TreeMap.Entry<K,V> lastReturned; 用于記錄本次返回的接待你
TreeMap.Entry<K,V> next; 指向下次要返回的節點
在向前或向后遍歷時需要知道上下邊界在哪,達到該邊界就代表超出范圍
final Object fenceKey;
int expectedModCount;
SubMapIterator(TreeMap.Entry<K,V> first,
TreeMap.Entry<K,V> 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<K,V> nextEntry() {
TreeMap.Entry<K,V> 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<K,V> prevEntry() {
TreeMap.Entry<K,V> 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<Map.Entry<K,V>> {
SubMapEntryIterator(TreeMap.Entry<K,V> first,
TreeMap.Entry<K,V> fence) {
super(first, fence);
}
public Map.Entry<K,V> next() {
return nextEntry();
}
public void remove() {
removeAscending();
}
}
Entry的降序迭代器
final class DescendingSubMapEntryIterator extends SubMapIterator<Map.Entry<K,V>> {
DescendingSubMapEntryIterator(TreeMap.Entry<K,V> last,
TreeMap.Entry<K,V> fence) {
super(last, fence);
}
public Map.Entry<K,V> next() {
return prevEntry();
}
public void remove() {
removeDescending();
}
}
鍵的升序迭代器
final class SubMapKeyIterator extends SubMapIterator<K>
implements Spliterator<K> {
SubMapKeyIterator(TreeMap.Entry<K,V> first,
TreeMap.Entry<K,V> fence) {
...............
}
鍵的降序迭代器
final class DescendingSubMapKeyIterator extends SubMapIterator<K>
implements Spliterator<K> {
DescendingSubMapKeyIterator(TreeMap.Entry<K,V> last,
TreeMap.Entry<K,V> fence) {
.....................
}
接下來看看NavigableSubMap的用途:
subMap
【fromKey, toKey)左閉右開,返回該區間范圍內的原map的視圖
public SortedMap<K,V> subMap(K fromKey, K toKey) {
return subMap(fromKey, true, toKey, false);
}
public NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
K toKey, boolean toInclusive) {
return new AscendingSubMap<>(this,
false, fromKey, fromInclusive,
false, toKey, toInclusive);
}
AscendingSubMap
static final class AscendingSubMap<K,V> extends NavigableSubMap<K,V> {
private static final long serialVersionUID = 912986545866124060L;
AscendingSubMap(TreeMap<K,V> 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<K,V> 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<K,V> 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<K,V> 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<K,V> descendingMap() {
NavigableMap<K,V> mv = descendingMapView;
return (mv != null) ? mv :
(descendingMapView =
new DescendingSubMap<>(m,
fromStart, lo, loInclusive,
toEnd, hi, hiInclusive));
}
Iterator<K> keyIterator() {
return new SubMapKeyIterator(absLowest(), absHighFence());
}
Spliterator<K> keySpliterator() {
return new SubMapKeyIterator(absLowest(), absHighFence());
}
Iterator<K> descendingKeyIterator() {
return new DescendingSubMapKeyIterator(absHighest(), absLowFence());
}
final class AscendingEntrySetView extends EntrySetView {
public Iterator<Map.Entry<K,V>> iterator() {
return new SubMapEntryIterator(absLowest(), absHighFence());
}
}
public Set<Map.Entry<K,V>> entrySet() {
EntrySetView es = entrySetView;
return (es != null) ? es : (entrySetView = new AscendingEntrySetView());
}
TreeMap.Entry<K,V> subLowest() { return absLowest(); }
TreeMap.Entry<K,V> subHighest() { return absHighest(); }
TreeMap.Entry<K,V> subCeiling(K key) { return absCeiling(key); }
TreeMap.Entry<K,V> subHigher(K key) { return absHigher(key); }
TreeMap.Entry<K,V> subFloor(K key) { return absFloor(key); }
TreeMap.Entry<K,V> subLower(K key) { return absLower(key); }
}
代碼設計時要低耦合高復用,NavigableSubMap就充分實現了這一準則。比如當你調用subMap得到了一個SortedMap對象,調用isEmpty()方法,Debug看看這一過程,它在多個方法與內部類中跳轉。
類似用AscendingSubMap來實現視圖的方法有:
大于fromKey的視圖,inclusive控制是否包括fromKey
public NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) {
return new AscendingSubMap<>(this,
false, fromKey, inclusive,
true, null, true);
}
小于toKey的視圖
public NavigableMap<K,V> headMap(K toKey, boolean inclusive) {
return new AscendingSubMap<>(this,
true, null, true,
false, toKey, inclusive);
}
descendingMap()
public NavigableMap<K, V> descendingMap() {
NavigableMap<K, V> km = descendingMap;
return (km != null) ? km :
(descendingMap = new DescendingSubMap<>(this,
true, null, true,
true, null, true));
}
DescendingSubMap
AscendingSubMap是升序的子map視圖,而DescendingSubMap與其相反,它是從上邊界往下邊界的順序,這就是二者的不同,除此之外它們的實現思路相同。
TreeMap還有很多的內部類,即相關操作,之后再分析.........