源碼解析(JDK1.8)之——TreeMap

1 TreeMap

1.1 底層結(jié)構(gòu)
TreeMap底層使用的數(shù)據(jù)結(jié)構(gòu)是紅黑樹

2 四個(gè)關(guān)注點(diǎn)

關(guān)注點(diǎn) 結(jié)論
TreeMap是否允許空 Key和Value都允許為空
TreeMap是否允許重復(fù)數(shù)據(jù) Key重復(fù)會(huì)覆蓋桩撮,Value允許重復(fù)
TreeMap是否有序 無序
TreeMap是否線程安全 非線程安全

3 TreeMap源碼解析

3.1 類的繼承關(guān)系

public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable

說明:繼承了抽象類AbstractMap,AbstractMap實(shí)現(xiàn)了Map接口扼菠,實(shí)現(xiàn)了部分方法。不能進(jìn)行實(shí)例化堵幽,實(shí)現(xiàn)了NavigableMap,Cloneable,Serializable接口玲献,其中NavigableMap是繼承自SortedMap的接口,定義了一系列規(guī)范倔既。

3.2 類的屬性

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{
    // 比較器呢蔫,用于控制Map中的元素順序
    private final Comparator<? super K> comparator;
    // 根節(jié)點(diǎn)
    private transient Entry<K,V> root;
    // 樹中結(jié)點(diǎn)個(gè)數(shù)
    private transient int size = 0;
    // 對樹進(jìn)行結(jié)構(gòu)性修改的次數(shù)
    private transient int modCount = 0;
}

說明:重點(diǎn)是比較器Comparator切心,此接口實(shí)現(xiàn)了對插入元素進(jìn)行排序。

3.3 類的構(gòu)造函數(shù)

1. TreeMap()型構(gòu)造函數(shù)

public TreeMap() {
        comparator = null;
}

2. TreeMap(Comparator<? super K>)型構(gòu)造函數(shù)

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

說明:用戶自定義了比較器片吊,可以按照用戶的邏輯進(jìn)行比較绽昏,確定元素的訪問順序。

3. TreeMap(Map<? extends K, ? extends V>)型構(gòu)造函數(shù)

public TreeMap(Map<? extends K, ? extends V> m) {
        comparator = null;
        putAll(m);
}
public void putAll(Map<? extends K, ? extends V> map) {
        int mapSize = map.size();
        if (size==0 && mapSize!=0 && map instanceof SortedMap) {
            Comparator<?> c = ((SortedMap<?,?>)map).comparator();
            if (c == comparator || (c != null && c.equals(comparator))) {
                ++modCount;
                try {
                    buildFromSorted(mapSize, map.entrySet().iterator(),
                                    null, null);
                } catch (java.io.IOException cannotHappen) {
                } catch (ClassNotFoundException cannotHappen) {
                }
                return;
            }
        }
        super.putAll(map);
}

4. TreeMap(SortedMap<K, ? extends V>)型構(gòu)造函數(shù)

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) {
        }
}

說明:傳入SortedMap型參數(shù)俏脊,實(shí)現(xiàn)SortedMap接口的類都會(huì)實(shí)現(xiàn)comparator方法而涉,用于返回比較器。

3.4 核心函數(shù)分析

1. put函數(shù)

public V put(K key, V value) {
        // 記錄根節(jié)點(diǎn)
        Entry<K,V> t = root;
        // 根節(jié)點(diǎn)為空
        if (t == null) {
            // 比較key
            compare(key, key); // type (and possibly null) check
            // 新生根節(jié)點(diǎn)
            root = new Entry<>(key, value, null);
            // 大小加1
            size = 1;
            // 修改次數(shù)加1
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // 獲取比較器
        Comparator<? super K> cpr = comparator;
        // 比較器不為空
        if (cpr != null) {
            // 找到元素合適的插入位置
            do {
                // parent賦值
                parent = t;
                // 比較key與元素的key值联予,在Comparator類的compare方法中可以實(shí)現(xiàn)我們自己的比較邏輯
                cmp = cpr.compare(key, t.key);
                // 小于結(jié)點(diǎn)key值啼县,向左子樹查找
                if (cmp < 0)
                    t = t.left;
                // 大于結(jié)點(diǎn)key值,向右子樹查找
                else if (cmp > 0)
                    t = t.right;
                // 表示相等沸久,直接更新結(jié)點(diǎn)的值
                else
                    return t.setValue(value);
            } while (t != null);
        }
        // 比較器為空
        else {
            // key為空季眷,拋出異常
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                // 取得K實(shí)現(xiàn)的比較器
                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);
        }
        // 新生一個(gè)結(jié)點(diǎn)
        Entry<K,V> e = new Entry<>(key, value, parent);
        // 根據(jù)比較結(jié)果決定存為左結(jié)點(diǎn)或右結(jié)點(diǎn)
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        // 插入后進(jìn)行修正
        fixAfterInsertion(e);
        // 大小加1
        size++;
        // 進(jìn)行了結(jié)構(gòu)性修改
        modCount++;
        return null;
}

說明:插入一個(gè)元素時(shí),若用戶自定義比較器卷胯,則會(huì)按照用戶自定義的邏輯確定元素的插入位置子刮,否則,將會(huì)使用K自身實(shí)現(xiàn)的比較器確定插入位置。

2. getEntry函數(shù)

final Entry<K,V> getEntry(Object key) {
        // 判斷比較器是否為空
        if (comparator != null)
            // 根據(jù)自定義的比較器來返回結(jié)果
            return getEntryUsingComparator(key);
        // 比較器為空
        // key為空挺峡,拋出異常
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            // 取得K自身實(shí)現(xiàn)了比較接口
            Comparable<? super K> k = (Comparable<? super K>) key;
        Entry<K,V> p = root;
        // 根據(jù)Comparable接口的compareTo函數(shù)來查找元素
        while (p != null) {
            int cmp = k.compareTo(p.key);
            if (cmp < 0)
                p = p.left;
            else if (cmp > 0)
                p = p.right;
            else
                return p;
        }
        return null;
}

說明:當(dāng)我們調(diào)用get函數(shù)時(shí)葵孤,實(shí)際上是委托g(shù)etEntry函數(shù)獲取元素,對于用戶自定義實(shí)現(xiàn)的Comparator比較器而言橱赠,是使用getEntryUsingComparator函數(shù)來完成獲取邏輯尤仍。

final Entry<K,V> getEntryUsingComparator(Object key) {
        @SuppressWarnings("unchecked")
            // 向下轉(zhuǎn)型
            K k = (K) key;
        // 取得比較器
        Comparator<? super K> cpr = comparator;
        // 比較器不為空
        if (cpr != null) {
            Entry<K,V> p = root;
            // 開始遍歷樹節(jié)點(diǎn)找到對應(yīng)的結(jié)點(diǎn)
            while (p != null) {
                int cmp = cpr.compare(k, p.key);
                // 小于結(jié)點(diǎn)key值,向左子樹查找
                if (cmp < 0)
                    p = p.left;
                // 大于結(jié)點(diǎn)key值狭姨,向右子樹查找
                else if (cmp > 0)
                    p = p.right;
                // 相等宰啦,找到,直接返回
                else
                    return p;
            }
        }
        return null;
}

說明:會(huì)根據(jù)用戶定義在compare函數(shù)里面的邏輯進(jìn)行元素的查找饼拍。

3. deleteEntry函數(shù)

private void deleteEntry(Entry<K,V> p) {
        // 結(jié)構(gòu)性修改
        modCount++;
        // 大小減1
        size--;
        // p的左右子結(jié)點(diǎn)均不為空
        if (p.left != null && p.right != null) {
            // 找到p結(jié)點(diǎn)的后繼
            Entry<K,V> s = successor(p);
            // 將p的值用其后繼結(jié)點(diǎn)的key-value替換赡模,并且用s指向其后繼
            p.key = s.key;
            p.value = s.value;
            p = s;
        } 

        // 開始進(jìn)行修正,具體的修正過程我們會(huì)在之后的數(shù)據(jù)結(jié)構(gòu)專區(qū)進(jìn)行講解
        // 現(xiàn)在可以看成是為了保持紅黑樹的特性师抄,提高性能
        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);

            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;
            }
        }
}

說明:deleteEntry函數(shù)會(huì)在remove函數(shù)中被調(diào)用漓柑,它完成了移除元素的主要工作,刪除該結(jié)點(diǎn)后會(huì)對紅黑樹進(jìn)行修正叨吮,此部分內(nèi)容以后會(huì)詳細(xì)講解辆布,同時(shí),在此函數(shù)中需要調(diào)用successor函數(shù)挤安,即找到該結(jié)點(diǎn)的后繼結(jié)點(diǎn)谚殊。具體函數(shù)代碼如下

// 找到后繼
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
        // t為null丧鸯,直接返回null
        if (t == null)
            return null;
        // 右孩子不為空
        else if (t.right != null) {
            // 找到右孩子的最底層的左孩子蛤铜,返回
            Entry<K,V> p = t.right;
            while (p.left != null)
                p = p.left;
            return p;
        } else { // 右孩子為空
            // 保存t的父節(jié)點(diǎn)
            Entry<K,V> p = t.parent;
            // 保存t結(jié)點(diǎn)
            Entry<K,V> ch = t;
            // 進(jìn)行回溯,找到后繼丛肢,直到p == null || ch != p.right
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
}

說明:當(dāng)結(jié)點(diǎn)的右子樹為空的時(shí)候围肥,進(jìn)行回溯可以找到該結(jié)點(diǎn)的后繼結(jié)點(diǎn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜂怎,一起剝皮案震驚了整個(gè)濱河市穆刻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杠步,老刑警劉巖氢伟,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異幽歼,居然都是意外死亡朵锣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門甸私,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诚些,“玉大人,你說我怎么就攤上這事皇型∥芘耄” “怎么了砸烦?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绞吁。 經(jīng)常有香客問我幢痘,道長,這世上最難降的妖魔是什么掀泳? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任雪隧,我火速辦了婚禮,結(jié)果婚禮上员舵,老公的妹妹穿的比我還像新娘脑沿。我一直安慰自己,他們只是感情好马僻,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布庄拇。 她就那樣靜靜地躺著,像睡著了一般韭邓。 火紅的嫁衣襯著肌膚如雪措近。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天女淑,我揣著相機(jī)與錄音瞭郑,去河邊找鬼。 笑死鸭你,一個(gè)胖子當(dāng)著我的面吹牛屈张,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播袱巨,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼阁谆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了愉老?” 一聲冷哼從身側(cè)響起场绿,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嫉入,沒想到半個(gè)月后焰盗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咒林,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年熬拒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片映九。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡梦湘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捌议,我是刑警寧澤哼拔,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站瓣颅,受9級特大地震影響倦逐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宫补,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一檬姥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粉怕,春花似錦健民、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稚晚,卻和暖如春崇堵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背客燕。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工鸳劳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人也搓。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓赏廓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親还绘。 傳聞我的和親對象是個(gè)殘疾皇子楚昭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容