概要
這一章,我們對TreeMap進行學習。
轉載請注明出處:http://www.cnblogs.com/skywang12345/admin/EditPosts.aspx?postid=3310928
第1部分 TreeMap介紹
TreeMap 簡介
TreeMap 是一個有序的key-value集合,它是通過紅黑樹實現的。
TreeMap 繼承於AbstractMap,所以它是一個Map,即一個key-value集合。
TreeMap 實現了NavigableMap接口,意味着它支持一系列的導航方法。比如返回有序的key集合。
TreeMap 實現了Cloneable接口,意味着它能被克隆。
TreeMap 實現了java.io.Serializable接口,意味着它支持序列化。
TreeMap基於紅黑樹(Red-Black tree)實現。該映射根據其鍵的自然順序進行排序,或者根據創建映射時提供的 Comparator 進行排序,具體取決於使用的構造方法。
TreeMap的基本操作 containsKey、get、put 和 remove 的時間復雜度是 log(n) 。
另外,TreeMap是非同步的。 它的iterator 方法返回的迭代器是fail-fastl的。
TreeMap的構造函數
// 默認構造函數。使用該構造函數,TreeMap中的元素按照自然排序進行排列。 TreeMap() // 創建的TreeMap包含Map TreeMap(Map<? extends K, ? extends V> copyFrom) // 指定Tree的比較器 TreeMap(Comparator<? super K> comparator) // 創建的TreeSet包含copyFrom TreeMap(SortedMap<K, ? extends V> copyFrom)
TreeMap的API
Entry<K, V> ceilingEntry(K key) K ceilingKey(K key) void clear() Object clone() Comparator<? super K> comparator() boolean containsKey(Object key) NavigableSet<K> descendingKeySet() NavigableMap<K, V> descendingMap() Set<Entry<K, V>> entrySet() Entry<K, V> firstEntry() K firstKey() Entry<K, V> floorEntry(K key) K floorKey(K key) V get(Object key) NavigableMap<K, V> headMap(K to, boolean inclusive) SortedMap<K, V> headMap(K toExclusive) Entry<K, V> higherEntry(K key) K higherKey(K key) boolean isEmpty() Set<K> keySet() Entry<K, V> lastEntry() K lastKey() Entry<K, V> lowerEntry(K key) K lowerKey(K key) NavigableSet<K> navigableKeySet() Entry<K, V> pollFirstEntry() Entry<K, V> pollLastEntry() V put(K key, V value) V remove(Object key) int size() SortedMap<K, V> subMap(K fromInclusive, K toExclusive) NavigableMap<K, V> subMap(K from, boolean fromInclusive, K to, boolean toInclusive) NavigableMap<K, V> tailMap(K from, boolean inclusive) SortedMap<K, V> tailMap(K fromInclusive)
第2部分 TreeMap數據結構
TreeMap的繼承關系
java.lang.Object ↳ java.util.AbstractMap<K, V> ↳ java.util.TreeMap<K, V> public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable {}
TreeMap與Map關系如下圖:
從圖中可以看出:
(01) TreeMap實現繼承於AbstractMap,並且實現了NavigableMap接口。
(02) TreeMap的本質是R-B Tree(紅黑樹),它包含幾個重要的成員變量: root, size, comparator。
root 是紅黑數的根節點。它是Entry類型,Entry是紅黑數的節點,它包含了紅黑數的6個基本組成成分:key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節點)、color(顏色)。Entry節點根據key進行排序,Entry節點包含的內容為value。
紅黑數排序時,根據Entry中的key進行排序;Entry中的key比較大小是根據比較器comparator來進行判斷的。
size是紅黑數中節點的個數。
關於紅黑數的具體算法,請參考"紅黑樹(一) 原理和算法詳細介紹"。
第3部分 TreeMap源碼解析(基於JDK1.6.0_45)
為了更了解TreeMap的原理,下面對TreeMap源碼代碼作出分析。我們先給出源碼內容,后面再對源碼進行詳細說明,當然,源碼內容中也包含了詳細的代碼注釋。讀者閱讀的時候,建議先看后面的說明,先建立一個整體印象;之后再閱讀源碼。

說明:
在詳細介紹TreeMap的代碼之前,我們先建立一個整體概念。
TreeMap是通過紅黑樹實現的,TreeMap存儲的是key-value鍵值對,TreeMap的排序是基於對key的排序。
TreeMap提供了操作“key”、“key-value”、“value”等方法,也提供了對TreeMap這顆樹進行整體操作的方法,如獲取子樹、反向樹。
后面的解說內容分為幾部分,
首先,介紹TreeMap的核心,即紅黑樹相關部分;
然后,介紹TreeMap的主要函數;
再次,介紹TreeMap實現的幾個接口;
最后,補充介紹TreeMap的其它內容。
TreeMap本質上是一顆紅黑樹。要徹底理解TreeMap,建議讀者先理解紅黑樹。關於紅黑樹的原理,可以參考:紅黑樹(一) 原理和算法詳細介紹
第3.1部分 TreeMap的紅黑樹相關內容
TreeMap中於紅黑樹相關的主要函數有:
1 數據結構
1.1 紅黑樹的節點顏色--紅色
private static final boolean RED = false;
1.2 紅黑樹的節點顏色--黑色
private static final boolean BLACK = true;
1.3 “紅黑樹的節點”對應的類。
static final class Entry<K,V> implements Map.Entry<K,V> { ... }
Entry包含了6個部分內容:key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節點)、color(顏色)
Entry節點根據key進行排序,Entry節點包含的內容為value。
2 相關操作
2.1 左旋
private void rotateLeft(Entry<K,V> p) { ... }
2.2 右旋
private void rotateRight(Entry<K,V> p) { ... }
2.3 插入操作
public V put(K key, V value) { ... }
2.4 插入修正操作
紅黑樹執行插入操作之后,要執行“插入修正操作”。
目的是:保紅黑樹在進行插入節點之后,仍然是一顆紅黑樹
private void fixAfterInsertion(Entry<K,V> x) { ... }
2.5 刪除操作
private void deleteEntry(Entry<K,V> p) { ... }
2.6 刪除修正操作
紅黑樹執行刪除之后,要執行“刪除修正操作”。
目的是保證:紅黑樹刪除節點之后,仍然是一顆紅黑樹
private void fixAfterDeletion(Entry<K,V> x) { ... }
關於紅黑樹部分,這里主要是指出了TreeMap中那些是紅黑樹的主要相關內容。具體的紅黑樹相關操作API,這里沒有詳細說明,因為它們僅僅只是將算法翻譯成代碼。
第3.2部分 TreeMap的構造函數
1 默認構造函數
使用默認構造函數構造TreeMap時,使用java的默認的比較器比較Key的大小,從而對TreeMap進行排序。
public TreeMap() { comparator = null; }
2 帶比較器的構造函數
public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; }
3 帶Map的構造函數,Map會成為TreeMap的子集
public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); }
該構造函數會調用putAll()將m中的所有元素添加到TreeMap中。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()); }
從中,我們可以看出putAll()就是將m中的key-value逐個的添加到TreeMap中。
4 帶SortedMap的構造函數,SortedMap會成為TreeMap的子集
public TreeMap(SortedMap<K, ? extends V> m) { comparator = m.comparator(); try { buildFromSorted(m.size(), m.entrySet().iterator(), null, null); } catch (java.io.IOException cannotHappen) { } catch (ClassNotFoundException cannotHappen) { } }
該構造函數不同於上一個構造函數,在上一個構造函數中傳入的參數是Map,Map不是有序的,所以要逐個添加。
而該構造函數的參數是SortedMap是一個有序的Map,我們通過buildFromSorted()來創建對應的Map。
buildFromSorted涉及到的代碼如下:

要理解buildFromSorted,重點說明以下幾點:
第一,buildFromSorted是通過遞歸將SortedMap中的元素逐個關聯。
第二,buildFromSorted返回middle節點(中間節點)作為root。
第三,buildFromSorted添加到紅黑樹中時,只將level == redLevel的節點設為紅色。第level級節點,實際上是buildFromSorted轉換成紅黑樹后的最底端(假設根節點在最上方)的節點;只將紅黑樹最底端的階段着色為紅色,其余都是黑色。
第3.3部分 TreeMap的Entry相關函數
TreeMap的 firstEntry()、 lastEntry()、 lowerEntry()、 higherEntry()、 floorEntry()、 ceilingEntry()、 pollFirstEntry() 、 pollLastEntry() 原理都是類似的;下面以firstEntry()來進行詳細說明
我們先看看firstEntry()和getFirstEntry()的代碼:
public Map.Entry<K,V> firstEntry() { return exportEntry(getFirstEntry()); } final Entry<K,V> getFirstEntry() { Entry<K,V> p = root; if (p != null) while (p.left != null) p = p.left; return p; }
從中,我們可以看出 firstEntry() 和 getFirstEntry() 都是用於獲取第一個節點。
但是,firstEntry() 是對外接口; getFirstEntry() 是內部接口。而且,firstEntry() 是通過 getFirstEntry() 來實現的。那為什么外界不能直接調用 getFirstEntry(),而需要多此一舉的調用 firstEntry() 呢?
先告訴大家原因,再進行詳細說明。這么做的目的是:防止用戶修改返回的Entry。getFirstEntry()返回的Entry是可以被修改的,但是經過firstEntry()返回的Entry不能被修改,只可以讀取Entry的key值和value值。下面我們看看到底是如何實現的。
(01) getFirstEntry()返回的是Entry節點,而Entry是紅黑樹的節點,它的源碼如下:
// 返回“紅黑樹的第一個節點” final Entry<K,V> getFirstEntry() { Entry<K,V> p = root; if (p != null) while (p.left != null) p = p.left; return p; }
從中,我們可以調用Entry的getKey()、getValue()來獲取key和value值,以及調用setValue()來修改value的值。
(02) firstEntry()返回的是exportEntry(getFirstEntry())。下面我們看看exportEntry()干了些什么?
static <K,V> Map.Entry<K,V> exportEntry(TreeMap.Entry<K,V> e) { return e == null? null : new AbstractMap.SimpleImmutableEntry<K,V>(e); }
實際上,exportEntry() 是新建一個AbstractMap.SimpleImmutableEntry類型的對象,並返回。
SimpleImmutableEntry的實現在AbstractMap.java中,下面我們看看AbstractMap.SimpleImmutableEntry是如何實現的,代碼如下:

從中,我們可以看出SimpleImmutableEntry實際上是簡化的key-value節點。
它只提供了getKey()、getValue()方法類獲取節點的值;但不能修改value的值,因為調用 setValue() 會拋出異常UnsupportedOperationException();
再回到我們之前的問題:那為什么外界不能直接調用 getFirstEntry(),而需要多此一舉的調用 firstEntry() 呢?
現在我們清晰的了解到:
(01) firstEntry()是對外接口,而getFirstEntry()是內部接口。
(02) 對firstEntry()返回的Entry對象只能進行getKey()、getValue()等讀取操作;而對getFirstEntry()返回的對象除了可以進行讀取操作之后,還可以通過setValue()修改值。
第3.4部分 TreeMap的key相關函數
TreeMap的firstKey()、lastKey()、lowerKey()、higherKey()、floorKey()、ceilingKey()原理都是類似的;下面以ceilingKey()來進行詳細說明
ceilingKey(K key)的作用是“返回大於/等於key的最小的鍵值對所對應的KEY,沒有的話返回null”,它的代碼如下:
public K ceilingKey(K key) { return keyOrNull(getCeilingEntry(key)); }
ceilingKey()是通過getCeilingEntry()實現的。keyOrNull()的代碼很簡單,它是獲取節點的key,沒有的話,返回null。
static <K,V> K keyOrNull(TreeMap.Entry<K,V> e) { return e == null? null : e.key; }
getCeilingEntry(K key)的作用是“獲取TreeMap中大於/等於key的最小的節點,若不存在(即TreeMap中所有節點的鍵都比key大),就返回null”。它的實現代碼如下:

第3.5部分 TreeMap的values()函數
values() 返回“TreeMap中值的集合”
values()的實現代碼如下:
public Collection<V> values() { Collection<V> vs = values; return (vs != null) ? vs : (values = new Values()); }
說明:從中,我們可以發現values()是通過 new Values() 來實現 “返回TreeMap中值的集合”。
那么Values()是如何實現的呢? 沒錯!由於返回的是值的集合,那么Values()肯定返回一個集合;而Values()正好是集合類Value的構造函數。Values繼承於AbstractCollection,它的代碼如下:

說明:從中,我們可以知道Values類就是一個集合。而 AbstractCollection 實現了除 size() 和 iterator() 之外的其它函數,因此只需要在Values類中實現這兩個函數即可。
size() 的實現非常簡單,Values集合中元素的個數=該TreeMap的元素個數。(TreeMap每一個元素都有一個值嘛!)
iterator() 則返回一個迭代器,用於遍歷Values。下面,我們一起可以看看iterator()的實現:
public Iterator<V> iterator() { return new ValueIterator(getFirstEntry()); }
說明: iterator() 是通過ValueIterator() 返回迭代器的,ValueIterator是一個類。代碼如下:
final class ValueIterator extends PrivateEntryIterator<V> { ValueIterator(Entry<K,V> first) { super(first); } public V next() { return nextEntry().value; } }
說明:ValueIterator的代碼很簡單,它的主要實現應該在它的父類PrivateEntryIterator中。下面我們一起看看PrivateEntryIterator的代碼:

說明:PrivateEntryIterator是一個抽象類,它的實現很簡單,只只實現了Iterator的remove()和hasNext()接口,沒有實現next()接口。
而我們在ValueIterator中已經實現的next()接口。
至此,我們就了解了iterator()的完整實現了。
第3.6部分 TreeMap的entrySet()函數
entrySet() 返回“鍵值對集合”。顧名思義,它返回的是一個集合,集合的元素是“鍵值對”。
下面,我們看看它是如何實現的?entrySet() 的實現代碼如下:
public Set<Map.Entry<K,V>> entrySet() { EntrySet es = entrySet; return (es != null) ? es : (entrySet = new EntrySet()); }
說明:entrySet()返回的是一個EntrySet對象。
下面我們看看EntrySet的代碼:

說明:
EntrySet是“TreeMap的所有鍵值對組成的集合”,而且它單位是單個“鍵值對”。
EntrySet是一個集合,它繼承於AbstractSet。而AbstractSet實現了除size() 和 iterator() 之外的其它函數,因此,我們重點了解一下EntrySet的size() 和 iterator() 函數
size() 的實現非常簡單,AbstractSet集合中元素的個數=該TreeMap的元素個數。
iterator() 則返回一個迭代器,用於遍歷AbstractSet。從上面的源碼中,我們可以發現iterator() 是通過EntryIterator實現的;下面我們看看EntryIterator的源碼:
final class EntryIterator extends PrivateEntryIterator<Map.Entry<K,V>> { EntryIterator(Entry<K,V> first) { super(first); } public Map.Entry<K,V> next() { return nextEntry(); } }
說明:和Values類一樣,EntryIterator也繼承於PrivateEntryIterator類。
第3.7部分 TreeMap實現的Cloneable接口
TreeMap實現了Cloneable接口,即實現了clone()方法。
clone()方法的作用很簡單,就是克隆一個TreeMap對象並返回。

第3.8部分 TreeMap實現的Serializable接口
TreeMap實現java.io.Serializable,分別實現了串行讀取、寫入功能。
串行寫入函數是writeObject(),它的作用是將TreeMap的“容量,所有的Entry”都寫入到輸出流中。
而串行讀取函數是readObject(),它的作用是將TreeMap的“容量、所有的Entry”依次讀出。
readObject() 和 writeObject() 正好是一對,通過它們,我能實現TreeMap的串行傳輸。

說到這里,就順便說一下“關鍵字transient”的作用
transient是Java語言的關鍵字,它被用來表示一個域不是該對象串行化的一部分。
Java的serialization提供了一種持久化對象實例的機制。當持久化對象時,可能有一個特殊的對象數據成員,我們不想用serialization機制來保存它。為了在一個特定對象的一個域上關閉serialization,可以在這個域前加上關鍵字transient。
當一個對象被串行化的時候,transient型變量的值不包括在串行化的表示中,然而非transient型的變量是被包括進去的。
第3.9部分 TreeMap實現的NavigableMap接口
firstKey()、lastKey()、lowerKey()、higherKey()、ceilingKey()、floorKey();
firstEntry()、 lastEntry()、 lowerEntry()、 higherEntry()、 floorEntry()、 ceilingEntry()、 pollFirstEntry() 、 pollLastEntry();
上面已經講解過這些API了,下面對其它的API進行說明。
1 反向TreeMap
descendingMap() 的作用是返回當前TreeMap的反向的TreeMap。所謂反向,就是排序順序和原始的順序相反。
我們已經知道TreeMap是一顆紅黑樹,而紅黑樹是有序的。
TreeMap的排序方式是通過比較器,在創建TreeMap的時候,若指定了比較器,則使用該比較器;否則,就使用Java的默認比較器。
而獲取TreeMap的反向TreeMap的原理就是將比較器反向即可!
理解了descendingMap()的反向原理之后,再講解一下descendingMap()的代碼。
// 獲取TreeMap的降序Map public NavigableMap<K, V> descendingMap() { NavigableMap<K, V> km = descendingMap; return (km != null) ? km : (descendingMap = new DescendingSubMap(this, true, null, true, true, null, true)); }
從中,我們看出descendingMap()實際上是返回DescendingSubMap類的對象。下面,看看DescendingSubMap的源碼:

從中,我們看出DescendingSubMap是降序的SubMap,它的實現機制是將“SubMap的比較器反轉”。
它繼承於NavigableSubMap。而NavigableSubMap是一個繼承於AbstractMap的抽象類;它包括2個子類——"(升序)AscendingSubMap"和"(降序)DescendingSubMap"。NavigableSubMap為它的兩個子類實現了許多公共API。
下面看看NavigableSubMap的源碼。

NavigableSubMap源碼很多,但不難理解;讀者可以通過源碼和注釋進行理解。
其實,讀完NavigableSubMap的源碼后,我們可以得出它的核心思想是:它是一個抽象集合類,為2個子類——"(升序)AscendingSubMap"和"(降序)DescendingSubMap"而服務;因為NavigableSubMap實現了許多公共API。它的最終目的是實現下面的一系列函數:
headMap(K toKey, boolean inclusive) headMap(K toKey) subMap(K fromKey, K toKey) subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) tailMap(K fromKey) tailMap(K fromKey, boolean inclusive) navigableKeySet() descendingKeySet()
第3.10部分 TreeMap其它函數
1 順序遍歷和逆序遍歷
TreeMap的順序遍歷和逆序遍歷原理非常簡單。
由於TreeMap中的元素是從小到大的順序排列的。因此,順序遍歷,就是從第一個元素開始,逐個向后遍歷;而倒序遍歷則恰恰相反,它是從最后一個元素開始,逐個往前遍歷。
我們可以通過 keyIterator() 和 descendingKeyIterator()來說明!
keyIterator()的作用是返回順序的KEY的集合,
descendingKeyIterator()的作用是返回逆序的KEY的集合。
keyIterator() 的代碼如下:
Iterator<K> keyIterator() { return new KeyIterator(getFirstEntry()); }
說明:從中我們可以看出keyIterator() 是返回以“第一個節點(getFirstEntry)” 為其實元素的迭代器。
KeyIterator的代碼如下:
final class KeyIterator extends PrivateEntryIterator<K> { KeyIterator(Entry<K,V> first) { super(first); } public K next() { return nextEntry().key; } }
說明:KeyIterator繼承於PrivateEntryIterator。當我們通過next()不斷獲取下一個元素的時候,就是執行的順序遍歷了。
descendingKeyIterator()的代碼如下:
Iterator<K> descendingKeyIterator() { return new DescendingKeyIterator(getLastEntry()); }
說明:從中我們可以看出descendingKeyIterator() 是返回以“最后一個節點(getLastEntry)” 為其實元素的迭代器。
再看看DescendingKeyIterator的代碼:
final class DescendingKeyIterator extends PrivateEntryIterator<K> { DescendingKeyIterator(Entry<K,V> first) { super(first); } public K next() { return prevEntry().key; } }
說明:DescendingKeyIterator繼承於PrivateEntryIterator。當我們通過next()不斷獲取下一個元素的時候,實際上調用的是prevEntry()獲取的上一個節點,這樣它實際上執行的是逆序遍歷了。
至此,TreeMap的相關內容就全部介紹完畢了。若有錯誤或紕漏的地方,歡迎指正!
第4部分 TreeMap遍歷方式
4.1 遍歷TreeMap的鍵值對
第一步:根據entrySet()獲取TreeMap的“鍵值對”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
// 假設map是TreeMap對象 // map中的key是String類型,value是Integer類型 Integer integ = null; Iterator iter = map.entrySet().iterator(); while(iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); // 獲取key key = (String)entry.getKey(); // 獲取value integ = (Integer)entry.getValue(); }
4.2 遍歷TreeMap的鍵
第一步:根據keySet()獲取TreeMap的“鍵”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
// 假設map是TreeMap對象 // map中的key是String類型,value是Integer類型 String key = null; Integer integ = null; Iterator iter = map.keySet().iterator(); while (iter.hasNext()) { // 獲取key key = (String)iter.next(); // 根據key,獲取value integ = (Integer)map.get(key); }
4.3 遍歷TreeMap的值
第一步:根據value()獲取TreeMap的“值”的集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。
// 假設map是TreeMap對象 // map中的key是String類型,value是Integer類型 Integer value = null; Collection c = map.values(); Iterator iter= c.iterator(); while (iter.hasNext()) { value = (Integer)iter.next(); }
TreeMap遍歷測試程序如下:

第5部分 TreeMap示例
下面通過實例來學習如何使用TreeMap

運行結果:
{one=8, three=4, two=2} next : one - 8 next : three - 4 next : two - 2 size: 3 contains key two : true contains key five : false contains value 0 : false tmap:{one=8, two=2} tmap is empty