本期系列文章的其他文章
JDK1.8 Collection知識點與代碼分析–整體框架
JDK1.8 Collection知識點與代碼分析–ArrayList&LinkedList
JDK1.8 Collection知識點與代碼分析–HashMap
JDK1.8 Collection知識點與代碼分析–LinkedHashMap
JDK1.8 Collection知識點與代碼分析–HashSet&TreeSet
這篇文章接着打開TreeMap的源碼進行分析. 首先TreeMap是util工具類中一個非常好用的有序映射, 自身是非線程安全的, 相比HashMap, 增加了對NavigableMap, sortedMap接口的實現, 增加了一系列基於鍵有序的api, 這裏先列舉幾個比較常用的
sortedMap
firstKey()
: 返回最小的keylastKey()
: 返回最大的keysubMap(from, to)
: 返回的是map的view, 左閉右開headMap(toKey, inclusive)
: 返回比toKey嚴格小(inclusive爲true時包括)的這部分map的viewtailMap(fromKey, inclusive)
: 返回比fromKey嚴格大(inclusive爲true時包括)的這部分map的view
NavigableMap(繼承SortedMap)
ceilingEntry(key)/ ceilingKey(key)
: 返回大於等於key參數的最小的元素, 如果沒有這樣的元素存在返回nullfloorEntry(key)/floorKey(key)
: 返回小於等於key參數的最大元素, 如果沒有這樣的元素, 返回nullhigherEntry/higherKey
: 和ceiling類似, 只不過嚴格大lowerEntry/lowerKey
: 和floor類似, 只不過嚴格小
TreeMap的核心數據結構是紅黑樹, 其紅黑樹的實現和HashMap
中的紅黑樹實現幾乎是一模一樣的, 只不過函數名和獲取父節點, 左右節點的方法有微小的差別, 以下貼出核心部分的代碼.
fixAfterInsertion
這個方法和HashMap
中的balanceInsertion
是一模一樣的, 下面的註釋看完沒有弄懂的可以看下這篇講hashMap的文章中的balanceInsertion
部分, 我在註釋中通過圖解的方式, 對每個部分解決的問題進行了梳理.
private void fixAfterInsertion(Entry<K,V> x) {
x.color = RED;
while (x != null && x != root && x.parent.color == RED) {
// x不是root節點, x的父節點爲紅節點
// x的父節點是祖先節點的左兒子
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
if (colorOf(y) == RED) {
// flip Color
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
if (x == rightOf(parentOf(x))) {
// x是右節點, x和父節點是紅節點, 所以先左旋
x = parentOf(x);
rotateLeft(x);
}
// 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) {
//flip
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;
}
TreeMap中一個需要注意的細節, 當它沒有指定comparator時, 是不允許null
作爲key的, 使用comparator
時, 取決於comparator
是否允許key爲null.
對於NavigatableMap
的api中的方法, 這裏只給出一個例子, 它們的思路都非常相似
final Entry<K,V> getHigherEntry(K key) {
Entry<K,V> p = root;
while (p != null) {
int cmp = compare(key, p.key);
if (cmp < 0) {
// 如果當前節點比key大, 就繼續往左找, 看是否能找到比當前更小的比key大的元素
if (p.left != null)
p = p.left;
else
return p;
} else {
if (p.right != null) {
p = p.right;
} else {
// 如果更大的沒有了, 往父節點找, 找到第一個比key大的父節點(回溯,找到第一個向左走的位置)
Entry<K,V> parent = p.parent;
Entry<K,V> ch = p;
while (parent != null && ch == parent.right) {
ch = parent;
parent = parent.parent;
}
return parent;
}
}
}
return null;
}
這個方法的返回值並不是直接返回的, 而是通過exportEntry
方法, 構造了一個SimpleImmutableEntry
的實例進行發佈, 保證安全性.
而對於SortedMap的api中方法, 它們返回的都是TreeMap的靜態內部類AscendingSubMap
, 這個靜態內部類又是繼承於TreeMap
的抽象靜態內部類NavigableSubMap
, 這個類的成員變量如下:
/**
* The backing map.
*/
final TreeMap<K,V> m;
/**
* Endpoints are represented as triples (fromStart, lo,
* loInclusive) and (toEnd, hi, hiInclusive). If fromStart is
* true, then the low (absolute) bound is the start of the
* backing map, and the other values are ignored. Otherwise,
* if loInclusive is true, lo is the inclusive bound, else lo
* is the exclusive bound. Similarly for the upper bound.
*/
final K lo, hi;
final boolean fromStart, toEnd;
final boolean loInclusive, hiInclusive;
可以看到, 它是成員TreeMap
的一個view, 內部維護了lo
,hi
兩個key, 它的ceiling, floor等方法, 都是在檢查是否在lo, hi範圍後, 調用TreeMap
的getCeilingEntry
等方法.