java原始碼分析之集合框架TreeMap 12
阿新 • • 發佈:2019-01-11
TreeMap
基於紅黑樹(Red-Black tree)的 NavigableMap
實現。該對映根據其鍵的自然順序進行排序,或者根據建立對映時提供的Comparator
進行排序,具體取決於使用的構造方法。
一、紅黑樹的介紹
紅黑樹,一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。
通過對任何一條從根到葉子的路徑上各個結點著色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。
紅黑樹的5個性質:
- 每個結點要麼是紅的要麼是黑的。
- 根結點是黑的。
- 每個葉結點(葉結點即指樹尾端NIL指標或NULL結點)都是黑的。
- 如果一個結點是紅的,那麼它的兩個兒子都是黑的。
- 對於任意結點而言,其到葉結點樹尾端NIL指標的每條路徑都包含相同數目的黑結點。
注意,此實現不是同步的(非執行緒安全的)。如果多個執行緒同時訪問一個對映,並且其中至少一個執行緒從結構上修改了該對映,則其必須 外部同步。(結構上的修改是指新增或刪除一個或多個對映關係的操作;僅改變與現有鍵關聯的值不是結構上的修改。)這一般是通過對自然封裝該對映的物件執行同步操作來完成的。如果不存在這樣的物件,則應該使用Collections.synchronizedSortedMap
方法來“包裝”該對映。最好在建立時完成這一操作,以防止對對映進行意外的不同步訪問,如下所示:
SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));
collection(由此類所有的“collection 檢視方法”返回)的 iterator 方法返回的迭代器都是 快速失敗 的:在迭代器建立之後,如果從結構上對對映進行修改,除非通過迭代器自身的 remove 方法,否則在其他任何時間以任何方式進行修改都將導致迭代器丟擲
ConcurrentModificationException
TreeMap:
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
從繼承關係可以看出,TreeMap繼承了AbstractMap,實現了NavigableMap介面,意味著它支援一系列的導航方法,比如返回有序的key集合。它還實現了Cloneable介面,意味著它能被克隆。另外也實現了Serializable介面,表示它支援序列化。
TreeMap 的API:
Entry實體類:
/------------------- Red-black樹-------------------------------/
private static final boolean RED = false;
private static final boolean BLACK = true;
//實現了Map.Entry<K,V> 的getKey(),getValue(),setValue(),equals(),hashCode()
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;
}
}
建構函式:
/*********************** 構造方法 **************************/
//使用鍵的自然順序構造一個新的、空的樹對映
public TreeMap() {
comparator = null; //比較器
}
//構造一個新的、空的樹對映,該對映根據給定比較器進行排序
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//構造一個與給定對映具有相同對映關係的新的樹對映,該對映根據其鍵的自然順序進行排序。
public TreeMap(Map<? extends K, ? extends V> m) { //Map 鍵的自然順序進行排序
comparator = null;
putAll(m);
}
// 構造一個與指定有序對映具有相同對映關係和相同排序順序的新的樹對映。
public TreeMap(SortedMap<K, ? extends V> m) { // 比較方法:新SortMap樹的comparator
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}
屬性:
private final Comparator<? super K> comparator; ////比較器
private transient Entry<K,V> root = null;//實體物件
private transient int size = 0;//紅黑樹節點個數,即Entry數
private transient int modCount = 0;//修改次數
private transient EntrySet entrySet = null;
private transient KeySet<K> navigableKeySet = null;
private transient NavigableMap<K,V> descendingMap = null;
TreeMap有四個建構函式,這裡分析一下第三個建構函式,內部呼叫了putAll方法,我們看一下putAll方法:
public void putAll(Map<? extends K, ? extends V> map) {
int mapSize = map.size();
//如果map是Sorted子類
if (size==0 && mapSize!=0 && map instanceof SortedMap) {
Comparator c = ((SortedMap)map).comparator();
//按照鍵的自然順序進行排序。
if (c == comparator || (c != null && c.equals(comparator))) {
++modCount;
try {// 將map中的元素逐個新增到TreeMap中,
buildFromSorted(mapSize, map.entrySet().iterator(),
null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
return;
}
}
//否則呼叫AbstractMap 的putAll()
super.putAll(map);
}
/* ----------------------AbstractMap 的putAll()-----------------*/
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
/* ----------------------AbstractMap 的putAll()-----------------*/
其他方法::
未完待續