TreeMap源碼分析

來源:互聯網
上載者:User

標籤:linked   oid   oss   private   中序   些許   區別   其它   儲存   

目錄

  • 概述
  • 儲存結構
  • 操作
    • 添加
    • 刪除
  • 遍曆
  • 擴容
  • 總結
概述

通常情況下儲存索引值對僅需要HashMap即可滿足需求, 但是HashMap有些許缺點, 比如: 1. 不能保證元素的順序, 因此產生了LinkedHashMap; 2. 不能對元素進行排序, 因此產生了本文中的TreeMap.

TreeMap實現了SortedMap介面, SortedMap介面中定義了排序機制, 以及或者頭元素或者尾元素等方法.

本文不會對每個方法進行說明, 只是撿重要的方法說明, 其它方法相信你看完本文就很容易懂的.

儲存結構

TreeMap雖然儲存了索引值對, 看錶面與HashMap沒什麼區別, 但是內部儲存結構卻截然相反.

儲存結構
HashMap 數組+鏈表/紅/黑樹狀結構
TreeMap 紅/黑樹狀結構
操作添加

在看源碼之前, 我們可以想一下, 對於排序二叉樹的插入操作是如何進行的?

從根開始比較, 如果新元素比樹中節點元素小, 就去與節點的左孩子比較大小, 如果新元素比樹中節點元素大, 就去與節點的右孩子比較大小, 那麼如果相同呢? 與HashMap中的處理一樣, 替換其value值.

源碼如下:

public V put(K key, V value) {    // 擷取根節點    Entry<K,V> t = root;    // 根節點為null --空樹    if (t == null) {        // 檢查key是否為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; // 得到比較子    // 這雷根據比較子是否為null進行了區分    if (cpr != null) { // 傳入了自訂比較子        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); // 相等則替換其value值        } while (t != null);    }    else {        if (key == null)            throw new NullPointerException();        // key如果沒有實現Comparable介面, 那麼這裡就會報錯        @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<K,V> e = new Entry<>(key, value, parent);    if (cmp < 0)        parent.left = e;    else        parent.right = e;    // 進行紅/黑樹狀結構的調整    fixAfterInsertion(e);    size++;    modCount++;    return null;}

上面代碼只是元素的插入代碼, 插入新元素之後, 可能會破壞紅/黑樹狀結構的性質, 紅/黑樹狀結構的調整操作參考另一片文章: 大戰紅/黑樹狀結構, 也就是上面代碼中的fixAfterInsertion(e);方法.

刪除

刪除操作需要注意一下, 如果刪除的節點有兩個孩子節點, 則不能直接刪除節點, 而是需要找到其前驅或者後繼節點來替換該節點, 然後把前驅或者後繼節點刪除掉即可.

private void deleteEntry(Entry<K,V> p) {    modCount++;    size--;    // 有兩個孩子節點, 這裡是去尋找後繼節點(也就是中根遍曆時該節點的後一個節點)    // 最後將待刪除的節點p指向後繼節點s    // If strictly internal, copy successor‘s element to p and then make p    // point to successor.    if (p.left != null && p.right != null) {        Entry<K,V> s = successor(p);        p.key = s.key;        p.value = s.value;        p = s;    } // p has 2 children    // 判斷待刪除節點是否有孩子節點    // Start fixup at replacement node, if it exists.    Entry<K,V> replacement = (p.left != null ? p.left : p.right);    if (replacement != null) { // 有孩子節點        // 連結孩子節點到父節點上        // Link replacement to parent        replacement.parent = p.parent;        if (p.parent == null)            root = replacement;        else if (p == p.parent.left)            p.parent.left  = replacement;        else            p.parent.right = replacement;        // Null out links so they are OK to use by fixAfterDeletion.        p.left = p.right = p.parent = null;        // Fix replacement        if (p.color == BLACK) // 如果刪除的是黑節點, 則需要進行紅/黑樹狀結構的調整            fixAfterDeletion(replacement);    } else if (p.parent == null) { // return if we are the only node.        root = null;    } else { //  No children. Use self as phantom replacement and unlink.        if (p.color == BLACK) // 如果刪除的是黑節點, 則需要進行紅/黑樹狀結構的調整            fixAfterDeletion(p);        // 刪除的是紅色節點, 進行null操作的賦值        if (p.parent != null) {            if (p == p.parent.left)                p.parent.left = null;            else if (p == p.parent.right)                p.parent.right = null;            p.parent = null;        }    }}

上面代碼也僅僅是排序二叉樹的刪除操作, 可能會破壞紅/黑樹狀結構的性質, 紅/黑樹狀結構的調整操作參考另一片文章: 大戰紅/黑樹狀結構, 也就是上面代碼中的fixAfterDeletion(p);方法.

遍曆

TreeMap的遍曆操作實際上就是二叉樹的中序遍曆操作.

遍曆操作時, 最重要的內部類就是EntryIterator, EntryIterator繼承PrivateEntryIterator, 重要的實現在PrivateEntryIterator中, 首先看到PrivateEntryIterator的構造方法需要傳入first元素, 也就是頭元素, 如何擷取頭元素呢? TreeMap使用了getFirstEntry()方法, 之後擷取下一個節點的時候使用尋找後繼的方法進行擷取即可.

懂了TreeMap中Entry的遍曆, 那麼key或者value的遍曆自然就知曉了.

擴容

TreeMap不涉及擴容操作, 因為它使用樹結構進行儲存.

總結

TreeMap除去紅/黑樹狀結構的調整之後, 就是非常簡單的二叉排序樹代碼, 學習時我們可以對其進行分離學習.

TreeMap源碼分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.