在平常的工做中,相比較與
HashMap
而言,TreeMap
的使用會少不少,即便在某些場景,須要使用到排序的Map時,也更多的是選擇LinkedHashMap
,那麼這個TreeMap
究竟是個怎樣的容器,又適用於什麼樣的應用場景呢?java
分析數據結構,最好的方式無疑是google+baidu+源碼了數據結構
看到源碼第一眼,就會發現與HashMap不一樣的是 TreeMap 實現的是 NavigableMap
, 而不是直接實現 Map
學習
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable { // .... }
有必要仔細看下這個 NavigableMap
,到底有些什麼特殊之處this
繼承體系: Map -> SortMap -> NavigbleMap
google
其中 SortMap
新增了下面幾個接口,目前也不知道具體有啥用,先翻譯下源碼註釋.net
// 既然叫作SortMap, 要排序的話,固然須要一個比較器了 Comparator<? super K> comparator(); SortedMap<K,V> subMap(K fromKey, K toKey); // 源碼註釋: 返回比Map中key比參數toKey小的全部kv對 SortedMap<K,V> headMap(K toKey); // 源碼註釋:返回比Map中key比參數fromKey大或相等的全部kv對 SortedMap<K,V> tailMap(K fromKey); K firstKey(); K lastKey();
接着就是 NavigableMap
定義的接口翻譯
// 返回Map中比傳入參數key小的kv對中,key最大的一個kv對 Map.Entry<K,V> lowerEntry(K key); K lowerKey(K key); // 返回Map中比傳入參數key小或相等的kv對中,key最大的一個kv對 Map.Entry<K,V> floorEntry(K key); K floorKey(K key); // 返回Map中比傳入參數key大或相等的kv對中,key最小的一個kv對 Map.Entry<K,V> ceilingEntry(K key); K ceilingKey(K key); // 返回Map中比傳入參數key大的kv對中,key最小的一個kv對 Map.Entry<K,V> higherEntry(K key); K higherKey(K key); Map.Entry<K,V> firstEntry(); Map.Entry<K,V> lastEntry(); Map.Entry<K,V> pollFirstEntry(); NavigableMap<K,V> descendingMap(); NavigableSet<K> navigableKeySet(); NavigableSet<K> descendingKeySet();
基本上這兩個接口就是提供了一些基於排序的獲取kv對的方式code
看下內部的成員變量,發現可能涉及到數據結構的就只有下面的這個root了blog
private transient Entry<K,V> root;
結合 TreeMap
的命名來看,底層的結構多半就真的是Tree
了,有樹的根節點,通常來說遍歷都是沒啥問題的排序
接下來看下 Entry的實現
static final class Entry<K,V> implements Map.Entry<K,V> { K key; V value; Entry<K,V> left; Entry<K,V> right; Entry<K,V> parent; boolean color = BLACK; /** * Make a new cell with given key, value, and parent, and with * {@code null} child links, and BLACK color. */ Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } /** * Returns the key. * * @return the key */ public K getKey() { return key; } /** * Returns the value associated with the key. * * @return the value associated with the key */ public V getValue() { return value; } /** * Replaces the value currently associated with the key with the given * value. * * @return the value associated with the key before this method was * called */ 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; } }
從Entry的內部成員變量能夠看出,這是一個二叉樹,且極有可能就是一顆紅黑樹(由於有個black
)
經過新增一個kv對的調用鏈,來分析下這棵樹,究竟是不是紅黑樹
將put方法撈出來, 而後補上註釋
public V put(K key, V value) { Entry<K,V> t = root; if (t == null) { // 奇怪的一行邏輯,感受並無什麼用 compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null); size = 1; modCount++; return null; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null) { // 下面這個循環能夠得出樹的左節點小於根小於右節點 // 而後找到新增的節點,做爲葉子節點在樹中的位置 // 注意這個相等時,直接更新了value值(這裏表示插入一條已存在的記錄) do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } else { // 比較器不存在的邏輯,這時要求key繼承 Comparable 接口 if (key == null) throw new NullPointerException(); @SuppressWarnings("unchecked") Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0) t = t.left; else if (cmp > 0) t = t.right; else return t.setValue(value); } while (t != null); } // 建立一個Entry節點 Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; // 紅黑樹的重排 fixAfterInsertion(e); size++; modCount++; return null; }
從添加邏輯,能夠得出結論:
因此新增一個kv對的邏輯就比較簡單了
遍歷樹,將kv對做爲葉子節點存在對應的位置
紅黑樹相關能夠做爲獨立的一個知識點,這裏不詳細展開,基本上經過上面的分析,能夠得出下面幾個點
紅黑值
相等掃一掃二維碼,關注 小灰灰blog