標籤:java collection
一、概述
TreeMap是基於紅/黑樹狀結構實現的。由於TreeMap實現了java.util.sortMap介面,集合中的映射關係是具有一定順序的,該映射根據其鍵的自然順序進行排序或者根據建立映射時提供的Comparator進行排序,具體取決於使用的構造方法。另外TreeMap中不允許鍵對象是null。
1、什麼是紅/黑樹狀結構?
紅/黑樹狀結構是一種特殊的二叉排序樹,主要有以下幾條基本性質:
- 每個節點都只能是紅色或者黑色
- 根節點是黑色
- 每個葉子節點是黑色的
- 如果一個節點是紅色的,則它的兩個子節點都是黑色的
- 從任意一個節點到每個葉子節點的所有路徑都包含相同數目的黑色節點
紅/黑樹狀結構的具體原理分析和演算法設計可參見博文:紅/黑樹狀結構的原理分析和演算法設計。
2、key的兩種排序方式
自然排序:TreeMap的所有key必須實現Comparable介面,並且所有key應該是同一個類的對象,否則將會拋ClassCastException異常
指定排序:這種排序需要在構造TreeMap時,傳入一個Comparator對象,該對象負責對TreeMap中的key進行排序
3、TreeMap類的繼承關係
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
其中,NavigableMap介面是擴充的SortMap,具有了針對給定搜尋目標返回最接近匹配項的導航方法。其方法 lowerEntry
、floorEntry
、ceilingEntry
和 higherEntry
分別返回與小於、小於等於、大於等於、大於給定鍵的鍵關聯的 Map.Entry
對象,如果不存在這樣的鍵,則返回 null
。類似地,方法 lowerKey
、floorKey
、ceilingKey
和 higherKey
只返回關聯的鍵。所有這些方法是為尋找條目而不是遍曆條目而設計的。
二、TreeMap源碼分析 1、儲存結構
TreeMap是基於紅/黑樹狀結構實現的,樹的節點定義如下:
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; //建構函式 Entry(K key, V value, Entry<K,V> parent) { this.key = key; this.value = value; this.parent = parent; } ......}
2、建構函式
TreeMap有四種建構函式,分別對應不同的參數。
//1.使用鍵的自然順序構造一個新的、空的樹映射 public TreeMap() { comparator = null; } //2.構造一個新的、空的樹映射,該映射根據給定比較子進行排序 public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } /3.構造一個與給定映射具有相同映射關係的新的樹映射,該映射根據其鍵的自然順序 進行排序 public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); } //4.構造一個與指定有序映射具有相同映射關係和相同排序次序的新的樹映射 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) { } }
3、TreeMap常用方法
V put(K key,V value):將鍵值對(key,value)添加到TreeMap中
public V put(K key, V value) { Entry<K,V> t = root; //若根節點為空白,則以(key,value)為參數建立節點 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) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0) //表示新增節點的key小於當前及節點的key,則以當前節點的左子節點作為新的當前節點 t = t.left; else if (cmp > 0) //表示新增節點的key大於當前及節點的key,則以當前節點的右子節點作為新的當前節點 t = t.right; else return t.setValue(value); //相等則覆蓋舊值 } while (t != null); } //如果cpr為空白,則採用預設的排序演算法進行建立TreeMap集合 else { 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); } //將新增節點當做parent的子節點 Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0) parent.left = e; else parent.right = e; //插入新的節點後,調用fixAfterInsertion調整紅/黑樹狀結構 fixAfterInsertion(e); size++; modCount++; return null; }
Set<Map.Entry<K,V>> entrySet():返回此映射中包含的映射關係的Set視圖
public Set<Map.Entry<K,V>> entrySet() { EntrySet es = entrySet; return (es != null) ? es : (entrySet = new EntrySet()); }
boolean remove(Object o): 如果此 TreeMap 中存在該鍵的映射關係,則將其刪除
public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> entry = (Map.Entry<K,V>) o; K 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; } }
三、TreeMap應用範例程式碼
public class TreeMapDemo{ public static void main(String[] args) { //使用鍵的自然順序構造一個新的、空的樹映射 TreeMap<String,String> tm=new TreeMap<>(); tm.put("001", "中國"); tm.put("003", "美國"); tm.put("002", "法國"); System.out.println("調用entrySet得到索引值對集:"); Set<Entry<String, String>> result=tm.entrySet(); for(Entry<String, String> result2:result) { System.out.println(result2.getKey()+"---"+result2.getValue()); } System.out.println("調用keySet得到鍵集:"); Set<String> result3=tm.keySet(); for(String str:result3) { System.out.println(str); } System.out.println("調用values得到值集:"); Collection result4=tm.values(); for(Object str:result4) System.out.println(str); //建立一個帶比較子的TreeMap TreeMap<String,String> tm2=new TreeMap<>(new ComparatorDemo()); tm2.put("001", "中國"); tm2.put("003", "美國"); tm2.put("002", "法國"); Set<Entry<String, String>> result5=tm2.entrySet(); for(Entry<String, String> result2:result5) { System.out.println(result2.getKey()+"---"+result2.getValue()); } }}
首先按照鍵的自然順序構建TreeMap,加入元素並遍曆:
然後建立一個比較子類,實現Comparator介面
public class ComparatorDemo implements Comparator<String>{ public int compare(String o1, String o2) { return 1; }}
在帶比較子的tm2中,按照與tm1相同的順序添加元素,此時再遍曆tm2,結果如下:
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java集合系列之TreeMap源碼分析