日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TreeMap源码

發(fā)布時間:2024/10/14 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TreeMap源码 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原博

1.介紹

所有已實現(xiàn)的接口:

Serializable, Cloneable, Map<K,V>, NavigableMap<K,V>, SortedMap<K,V>

public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable
  • TreeMap 是一個有序的key-value集合,它是通過紅黑樹實現(xiàn)的。
    • 該映射根據(jù)其鍵的自然順序進行排序,或者根據(jù)創(chuàng)建映射時提供的 Comparator 進行排序,具體取決于使用的構(gòu)造方法。
  • TreeMap 繼承于AbstractMap,所以它是一個Map,即一個key-value集合。
  • TreeMap 實現(xiàn)了NavigableMap接口,意味著它支持一系列的導航方法。比如返回有序的key集合。
  • TreeMap 實現(xiàn)了Cloneable接口,意味著它能被克隆
  • TreeMap 實現(xiàn)了java.io.Serializable接口,意味著它支持序列化

此實現(xiàn)為 containsKey、get、put 和 remove 操作提供受保證的 log(n) 時間開銷。

此實現(xiàn)不是同步的。

2.構(gòu)造方法

TreeMap():使用鍵的自然順序構(gòu)造一個新的、空的樹映射。

TreeMap(Comparator<? super K>?comparator):構(gòu)造一個新的、空的樹映射,該映射根據(jù)給定比較器進行排序。

TreeMap(Map<? extends K,? extends V>?m):??構(gòu)造一個與給定映射具有相同映射關(guān)系的新的樹映射,該映射根據(jù)其鍵的自然順序 進行排序。

TreeMap(SortedMap<K,? extends V>?m):構(gòu)造一個與指定有序映射具有相同映射關(guān)系和相同排序順序的新的樹映射。

3.TreeMap和Map的關(guān)系

TreeMap的本質(zhì)是R-B Tree(紅黑樹),它包含幾個重要的成員變量: root, size, comparator。

  • root 是紅黑數(shù)的根節(jié)點。它是Entry類型。
    • Entry是紅黑數(shù)的節(jié)點,它包含了紅黑數(shù)的6個基本組成成分:key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節(jié)點)、color(顏色)。
    • Entry節(jié)點根據(jù)key進行排序,Entry節(jié)點包含的內(nèi)容為value。
    • 紅黑數(shù)排序時,根據(jù)Entry中的key進行排序。
    • Entry中的key比較大小是根據(jù)比較器comparator來進行判斷的。
  • size是紅黑數(shù)中節(jié)點的個數(shù)。

4.源碼分析

4.1TreeMap的紅黑樹相關(guān)內(nèi)容

4.1.1數(shù)據(jù)結(jié)構(gòu)

//紅黑樹的節(jié)點顏色--紅色 private static final boolean RED = false;//紅黑樹的節(jié)點顏色--黑色 private static final boolean BLACK = true;// “紅黑樹的節(jié)點”對應(yīng)的類。 static final class Entry<K,V> implements Map.Entry<K,V> { ... }

Entry包含了6個部分內(nèi)容:

  • key(鍵)
  • value(值)
  • left(左孩子)
  • right(右孩子)
  • parent(父節(jié)點)
  • color(顏色)

Entry節(jié)點根據(jù)key進行排序,Entry節(jié)點包含的內(nèi)容為value。

4.1.2 相關(guān)操作

//左旋 private void rotateLeft(Entry<K,V> p) { ... }//右旋 private void rotateRight(Entry<K,V> p) { ... }//插入操作 public V put(K key, V value) { ... }/*插入修正操作 紅黑樹執(zhí)行插入操作之后,要執(zhí)行“插入修正操作”。 目的是:保紅黑樹在進行插入節(jié)點之后,仍然是一顆紅黑樹*/ private void fixAfterInsertion(Entry<K,V> x) { ... }//刪除操作 private void deleteEntry(Entry<K,V> p) { ... }/*刪除修正操作 紅黑樹執(zhí)行刪除之后,要執(zhí)行“刪除修正操作”。 目的是保證:紅黑樹刪除節(jié)點之后,仍然是一顆紅黑樹*/ private void fixAfterDeletion(Entry<K,V> x) { ... }

4.2構(gòu)造函數(shù)

4.2.1 默認構(gòu)造函數(shù)

使用默認構(gòu)造函數(shù)構(gòu)造TreeMap時,使用java的默認的比較器比較Key的大小,從而對TreeMap進行排序。

public TreeMap() {comparator = null; }

4.2.2 帶比較器的構(gòu)造函數(shù)

public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator; }

4.2.3?帶Map的構(gòu)造函數(shù),Map會成為TreeMap的子集

public TreeMap(Map<? extends K, ? extends V> m) {comparator = null;putAll(m); }

該構(gòu)造函數(shù)會調(diào)用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.2.4?帶SortedMap的構(gòu)造函數(shù),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) {} }

該構(gòu)造函數(shù)不同于上一個構(gòu)造函數(shù),在上一個構(gòu)造函數(shù)中傳入的參數(shù)是MapMap不是有序的,所以要逐個添加

而該構(gòu)造函數(shù)的參數(shù)是SortedMap是一個有序的Map,我們通過buildFromSorted()來創(chuàng)建對應(yīng)的Map。

buildFromSorted涉及到的代碼如下:

// 根據(jù)已經(jīng)一個排好序的map創(chuàng)建一個TreeMap// 將map中的元素逐個添加到TreeMap中,并返回map的中間元素作為根節(jié)點。private final Entry<K,V> buildFromSorted(int level, int lo, int hi,int redLevel,Iterator it,java.io.ObjectInputStream str,V defaultVal)throws java.io.IOException, ClassNotFoundException {if (hi < lo) return null;// 獲取中間元素int mid = (lo + hi) / 2;Entry<K,V> left = null;// 若lo小于mid,則遞歸調(diào)用獲取(middel的)左孩子。if (lo < mid)left = buildFromSorted(level+1, lo, mid - 1, redLevel,it, str, defaultVal);// 獲取middle節(jié)點對應(yīng)的key和valueK key;V value;if (it != null) {if (defaultVal==null) {Map.Entry<K,V> entry = (Map.Entry<K,V>)it.next();key = entry.getKey();value = entry.getValue();} else {key = (K)it.next();value = defaultVal;}} else { // use streamkey = (K) str.readObject();value = (defaultVal != null ? defaultVal : (V) str.readObject());}// 創(chuàng)建middle節(jié)點Entry<K,V> middle = new Entry<K,V>(key, value, null);// 若當前節(jié)點的深度=紅色節(jié)點的深度,則將節(jié)點著色為紅色。if (level == redLevel)middle.color = RED;// 設(shè)置middle為left的父親,left為middle的左孩子if (left != null) {middle.left = left;left.parent = middle;}if (mid < hi) {// 遞歸調(diào)用獲取(middel的)右孩子。Entry<K,V> right = buildFromSorted(level+1, mid+1, hi, redLevel,it, str, defaultVal);// 設(shè)置middle為left的父親,left為middle的左孩子middle.right = right;right.parent = middle;}return middle;}

要理解buildFromSorted,重點說明以下幾點:

  • buildFromSorted是通過遞歸將SortedMap中的元素逐個關(guān)聯(lián)
  • buildFromSorted返回middle節(jié)點(中間節(jié)點)作為root。
  • buildFromSorted添加到紅黑樹中時,只將level == redLevel的節(jié)點設(shè)為紅色。
    • 第level級節(jié)點,實際上是buildFromSorted轉(zhuǎn)換成紅黑樹后的最底端(假設(shè)根節(jié)點在最上方)的節(jié)點
    • 只將紅黑樹最底端的階段著色為紅色,其余都是黑色。

4.3?TreeMap的Entry相關(guān)函數(shù)

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() 都是用于獲取第一個節(jié)點。

  • firstEntry() 是對外接口
  • getFirstEntry() 是內(nèi)部接口
  • firstEntry() 是通過 getFirstEntry() 來實現(xiàn)的。

那為什么外界不能直接調(diào)用 getFirstEntry(),而需要多此一舉的調(diào)用 firstEntry() 呢?

這么做的目的是:防止用戶修改返回的Entry。

  • getFirstEntry()返回的Entry是可以被修改的,
  • 但是經(jīng)過firstEntry()返回的Entry不能被修改,只可以讀取Entry的key值和value值。

下面我們看看到底是如何實現(xiàn)的。

4.3.1?getFirstEntry()

getFirstEntry()返回的是Entry節(jié)點,而Entry是紅黑樹的節(jié)點,它的源碼如下:

// 返回“紅黑樹的第一個節(jié)點” final Entry<K,V> getFirstEntry() {Entry<K,V> p = root;if (p != null)while (p.left != null)p = p.left;return p; }

從中,我們可以調(diào)用Entry的getKey()、getValue()來獲取key和value值,以及調(diào)用setValue()來修改value的值。

4.3.2?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的實現(xiàn)在AbstractMap.java中.

下面我們看看AbstractMap.SimpleImmutableEntry是如何實現(xiàn)的,代碼如下:

public static class SimpleImmutableEntry<K,V> implements Entry<K,V>, java.io.Serializable {private static final long serialVersionUID = 7138329143949025153L;private final K key;private final V value;public SimpleImmutableEntry(K key, V value) {this.key = key;this.value = value;}public SimpleImmutableEntry(Entry<? extends K, ? extends V> entry) {this.key = entry.getKey();this.value = entry.getValue();}public K getKey() {return key;}public V getValue() {return value;}public V setValue(V value) {throw new UnsupportedOperationException();}public boolean equals(Object o) {if (!(o instanceof Map.Entry))return false;Map.Entry e = (Map.Entry)o;return eq(key, e.getKey()) && eq(value, e.getValue());}public int hashCode() {return (key == null ? 0 : key.hashCode()) ^(value == null ? 0 : value.hashCode());}public String toString() {return key + "=" + value;} }

從中,我們可以看出SimpleImmutableEntry實際上是簡化的key-value節(jié)點

它只提供了getKey()、getValue()方法類獲取節(jié)點的值;但不能修改value的值,因為調(diào)用 setValue() 會拋出異常UnsupportedOperationException();

之前的問題:

為什么外界不能直接調(diào)用 getFirstEntry(),而需要多此一舉的調(diào)用 firstEntry() 呢?

  • firstEntry()是對外接口,而getFirstEntry()是內(nèi)部接口
  • 對firstEntry()返回的Entry對象只能進行getKey()、getValue()等讀取操作;而對getFirstEntry()返回的對象除了可以進行讀取操作之后,還可以通過setValue()修改值。
  • 4.4?TreeMap的key相關(guān)函數(shù)

    TreeMap的firstKey()、lastKey()、lowerKey()、higherKey()、floorKey()、ceilingKey()原理都是類似的;

    下面以ceilingKey()來進行詳細說明。

    ceilingKey(K key)的作用:返回大于/等于key的最小的鍵值對所對應(yīng)的KEY,沒有的話返回null。

    它的代碼如下:

    public K ceilingKey(K key) {return keyOrNull(getCeilingEntry(key)); }

    ceilingKey()是通過getCeilingEntry()實現(xiàn)的。keyOrNull()的代碼很簡單,它是獲取節(jié)點的key,沒有的話,返回null。

    static <K,V> K keyOrNull(TreeMap.Entry<K,V> e) {return e == null? null : e.key; }

    getCeilingEntry(K key):獲取TreeMap中大于/等于key的最小的節(jié)點,若不存在(即TreeMap中所有節(jié)點的鍵都比key大),就返回null。

    它的實現(xiàn)代碼如下:

    final Entry<K,V> getCeilingEntry(K key) {Entry<K,V> p = root;while (p != null) {int cmp = compare(key, p.key);// 情況一:若“p的key” > key。// 若 p 存在左孩子,則設(shè) p=“p的左孩子”;// 否則,返回pif (cmp < 0) {if (p.left != null)p = p.left;elsereturn p;// 情況二:若“p的key” < key。} else if (cmp > 0) {// 若 p 存在右孩子,則設(shè) p=“p的右孩子”if (p.right != null) {p = p.right;} else {// 若 p 不存在右孩子,則找出 p 的后繼節(jié)點,并返回// 注意:這里返回的 “p的后繼節(jié)點”有2種可能性:第一,null;第二,TreeMap中大于key的最小的節(jié)點。// 理解這一點的核心是,getCeilingEntry是從root開始遍歷的。// 若getCeilingEntry能走到這一步,那么,它之前“已經(jīng)遍歷過的節(jié)點的key”都 > key。// 能理解上面所說的,那么就很容易明白,為什么“p的后繼節(jié)點”有2種可能性了。Entry<K,V> parent = p.parent;Entry<K,V> ch = p;while (parent != null && ch == parent.right) {ch = parent;parent = parent.parent;}return parent;}// 情況三:若“p的key” = key。} elsereturn p;}return null; }

    4.5 TreeMap其它函數(shù)

    4.5.1? 順序遍歷和逆序遍歷

    TreeMap的順序遍歷和逆序歷原理非常簡單。

    由于TreeMap中的元素是從小到大的順序排列的因此:

    • 順序遍歷,就是從第一個元素開始,逐個向后遍歷;
    • 而倒序遍歷則恰恰相反,它是從最后一個元素開始,逐個往前遍歷。

    我們可以通過 keyIterator() 和 descendingKeyIterator()來說明!

    • keyIterator():返回順序的KEY的集合,
    • descendingKeyIterator():返回逆序的KEY的集合。

    keyIterator() 的代碼如下:

    Iterator<K> keyIterator() {return new KeyIterator(getFirstEntry()); }

    說明:從中我們可以看出keyIterator() 是返回以“第一個節(jié)點(getFirstEntry)” 為其實元素的迭代器

    KeyIterator的代碼如下:

    final class KeyIterator extends PrivateEntryIterator<K> {KeyIterator(Entry<K,V> first) {super(first);}public K next() {return nextEntry().key;} }

    說明:KeyIterator繼承于PrivateEntryIterator。當我們通過next()不斷獲取下一個元素的時候,就是執(zhí)行的順序遍歷了。

    descendingKeyIterator()的代碼如下:

    Iterator<K> descendingKeyIterator() {return new DescendingKeyIterator(getLastEntry()); }

    說明:從中我們可以看出descendingKeyIterator() 是返回以“最后一個節(jié)點(getLastEntry)” 為其實元素的迭代器

    再看看DescendingKeyIterator的代碼:

    5.TreeMap遍歷方式

    5.1?遍歷TreeMap的鍵值對

  • 根據(jù)entrySet()獲取TreeMap的“鍵值對”的Set集合。
  • 通過Iterator迭代器遍歷“第一步”得到的集合。
  • // 假設(shè)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();// 獲取keykey = (String)entry.getKey();// 獲取valueinteg = (Integer)entry.getValue(); }

    5.2 遍歷TreeMap的鍵

  • 根據(jù)keySet()獲取TreeMap的“鍵”的Set集合。
  • 通過Iterator迭代器遍歷“第一步”得到的集合。
  • // 假設(shè)map是TreeMap對象 // map中的key是String類型,value是Integer類型 String key = null; Integer integ = null; Iterator iter = map.keySet().iterator(); while (iter.hasNext()) {// 獲取keykey = (String)iter.next();// 根據(jù)key,獲取valueinteg = (Integer)map.get(key); }

    5.3 遍歷TreeMap的值

  • 根據(jù)value()獲取TreeMap的“值”的集合。
  • 通過Iterator迭代器遍歷“第一步”得到的集合。
  • // 假設(shè)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(); }

    ?

    ?

    ?

    總結(jié)

    以上是生活随笔為你收集整理的TreeMap源码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。