Java1.8-TreeMap源碼解析

概述

??從API文檔上我們可以知道鞭执,TreeMap是基于紅黑樹(Red-Black tree)來實(shí)現(xiàn)的,該數(shù)據(jù)結(jié)構(gòu)最重要的特點(diǎn)就是可排序芒粹。默認(rèn)情況下根據(jù)key的自然順序進(jìn)行排序兄纺,不過我們也可以自定義排序的順序。
??至于紅黑樹的相關(guān)實(shí)現(xiàn)化漆,就先不講了估脆,由于有些復(fù)雜,并且本人對(duì)紅黑樹還沒有完成吃透座云,但紅黑樹的性質(zhì)大家起碼還是要知道的疙赠。

繼承關(guān)系
public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable

// NavigableMap接口繼承了SortedMap
public interface NavigableMap<K,V> extends SortedMap<K,V> {

??TreeMap實(shí)現(xiàn)了NavigableMap接口,而通過查看NavigableMap則是繼承了SortedMap接口疙教。由此我們可以知道,TreeMap排序的功能是基于NavigableMap也就是SortedMap的伞租。

NavigableMap贞谓,這個(gè)接口翻譯過來可以理解為導(dǎo)航相關(guān),該接口提供了許多個(gè)導(dǎo)航相關(guān)的方法葵诈,比如lowerKey裸弦,ceilingKey,higherKey等作喘,同樣理疙,TreeMap實(shí)現(xiàn)該接口后,除了實(shí)現(xiàn)這些方法泞坦,也自定義類了一些導(dǎo)航相關(guān)的方法窖贤,大家可自行根據(jù)API進(jìn)行查看。

而這些排序相關(guān)的操作,基本上都離不開JDK排序相關(guān)的兩個(gè)接口:Comparator和Comparable赃梧。

屬性
/**
 * 比較器
 */
private final Comparator<? super K> comparator;

/**
 * 紅黑樹的紅黑節(jié)點(diǎn)
 */
private transient Entry<K,V> root;

/**
 * 容量大小
 */
private transient int size = 0;

/**
 * 結(jié)構(gòu)性修改
 */
private transient int modCount = 0;

/**
 * 紅黑樹節(jié)點(diǎn)類型
 */
static final class Entry<K,V> implements Map.Entry<K,V> {
    K key;
    V value;
    // 指向左子樹
    Entry<K,V> left;
    // 指向右子樹
    Entry<K,V> right;
    // 指向父節(jié)點(diǎn)
    Entry<K,V> parent;
    boolean color = BLACK;
}

// 紅黑樹節(jié)點(diǎn)-紅顏色
private static final boolean RED   = false;
// 紅黑樹節(jié)點(diǎn)-黑顏色
private static final boolean BLACK = true;
構(gòu)造方法
/**
 * 默認(rèn)構(gòu)造器滤蝠,使用默認(rèn)排序機(jī)制
 */
public TreeMap() {
    comparator = null;
}
/**
 * 自定義比較器的構(gòu)造方法
 */
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}

/**
 * 將Map構(gòu)造為TreeMap
 */
public TreeMap(Map<? extends K, ? extends V> m) {
    comparator = null;
    putAll(m);
}
/**
 * 構(gòu)造SortedMap對(duì)象為TreeMap,并使用SortedMap的比較器
 */
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) {
    }
}

方法

put方法
/**
 * 紅黑樹的put操作大體上分為兩步:構(gòu)建二叉排序樹授嘀,平衡二叉排序樹
 * 構(gòu)建的時(shí)候物咳,如果用戶自定義了比較器,則會(huì)按照用戶定義的規(guī)則來蹄皱,否則將按照默認(rèn)的比較規(guī)則來插入數(shù)據(jù)览闰;
 */
public V put(K key, V value) {
    // 二叉樹當(dāng)前節(jié)點(diǎn)
    Entry<K,V> t = root;
    // 如果二叉樹為null,直接插入
    if (t == null) {
        compare(key, key); // type (and possibly null) check

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    // 使用cmp來表示排序返回的結(jié)果
    int cmp;
    // 父節(jié)點(diǎn)
    Entry<K,V> parent;
    // 比較器
    Comparator<? super K> cpr = comparator;
    // 如果比較器不為空巷折,按照用戶指定比較器進(jìn)行循環(huán)比較压鉴,確定元素插入位置
    if (cpr != null) {
        do {
            parent = t;
            // 對(duì)key進(jìn)行比較
            cmp = cpr.compare(key, t.key);
            // 比較結(jié)果小于0,表示新增節(jié)點(diǎn)的key小于當(dāng)前節(jié)點(diǎn)的key盔几,則以當(dāng)前節(jié)點(diǎn)的左子節(jié)點(diǎn)作為新的當(dāng)前節(jié)點(diǎn)
            if (cmp < 0)
                t = t.left;
            // 比較結(jié)果大于0晴弃,表示新增節(jié)點(diǎn)的key大于當(dāng)前節(jié)點(diǎn)的key,則以當(dāng)前節(jié)點(diǎn)的右子節(jié)點(diǎn)作為新的當(dāng)前節(jié)點(diǎn)
            else if (cmp > 0)
                t = t.right;
            // 比較結(jié)果等于0逊拍,說明key相等上鞠,覆蓋舊值,返回新值
            else
                return t.setValue(value);
        } while (t != null);
    }
    // 如果比較器為null
    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);
    }
    // 新增節(jié)點(diǎn)設(shè)為parent的子節(jié)點(diǎn)
    Entry<K,V> e = new Entry<>(key, value, parent);
    // 如果新增節(jié)點(diǎn)的key小于parent的key芯丧,則當(dāng)做左子節(jié)點(diǎn)
    if (cmp < 0)
        parent.left = e;
    // 否則芍阎,右子節(jié)點(diǎn)
    else
        parent.right = e;
    // 插入完成,對(duì)二叉樹進(jìn)行平衡操作
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}
get方法
/**
 * get方法相對(duì)簡單些缨恒,只是對(duì)二叉樹的比較遍歷而已
 */
final Entry<K,V> getEntry(Object key) {
    // 如果比較器不為空谴咸,則按照自定義規(guī)則遍歷二叉樹
    if (comparator != null)
        return getEntryUsingComparator(key);
    if (key == null)
        throw new NullPointerException();
    @SuppressWarnings("unchecked")
        Comparable<? super K> k = (Comparable<? super K>) key;
    Entry<K,V> p = root;
    // 按照默認(rèn)比較規(guī)則遍歷二叉樹
    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;
}
remove方法
/**
 * 這里刪除操作其實(shí)很微妙,并不是直接刪除骗露,而是用被刪除節(jié)點(diǎn)的后繼節(jié)點(diǎn)來代替被刪掉的節(jié)點(diǎn)岭佳,然后修復(fù)被刪除節(jié)點(diǎn)的結(jié)構(gòu)來進(jìn)行操作的
 */
private void deleteEntry(Entry<K,V> p) {
    modCount++;
    size--;

    // p節(jié)點(diǎn)為要?jiǎng)h除的節(jié)點(diǎn),如果p節(jié)點(diǎn)的左右子節(jié)點(diǎn)都不為空
    if (p.left != null && p.right != null) {
        // 找到p節(jié)點(diǎn)的后繼節(jié)點(diǎn)萧锉,這里不是直接刪除p節(jié)點(diǎn)珊随,而是將p的后繼節(jié)點(diǎn)來代替要?jiǎng)h除的節(jié)點(diǎn),然后再進(jìn)行修復(fù)操作
        Entry<K,V> s = successor(p);
        p.key = s.key;
        p.value = s.value;
        p = s;
    } // p has 2 children

    // 開始修復(fù)操作
    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;
        }
    }
}

/**
 * 獲取后繼節(jié)點(diǎn)柿隙,其實(shí)這是一個(gè)類似中序遍歷的方式
 */
static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
    if (t == null)
        return null;
    else if (t.right != null) {
        // 節(jié)點(diǎn)的右子樹不為空叶洞,后繼結(jié)點(diǎn)就是右子樹的最左節(jié)點(diǎn)
        // 因?yàn)樽钭笞訕涫怯易訕涞淖钚」?jié)點(diǎn)
        Entry<K,V> p = t.right;
        while (p.left != null)
            p = p.left;
        return p;
    } else {
        // 右子樹為空,則尋找當(dāng)前節(jié)點(diǎn)左子樹的第一個(gè)父節(jié)點(diǎn)
        Entry<K,V> p = t.parent;
        Entry<K,V> ch = t;
        while (p != null && ch == p.right) {
            ch = p;
            p = p.parent;
        }
        return p;
    }
}

獲取后繼節(jié)點(diǎn)可參考:
Java TreeMap工作原理及實(shí)現(xiàn)

??如果要深入理解TreeMap的紅黑樹實(shí)現(xiàn)禀崖,可以搜索《史上最簡單清晰的紅黑樹講解》系列博客衩辟。

文章參考自:
TreeMap源碼分析(jdk1.8)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市波附,隨后出現(xiàn)的幾起案子艺晴,更是在濱河造成了極大的恐慌昼钻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件财饥,死亡現(xiàn)場離奇詭異换吧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)钥星,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門沾瓦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谦炒,你說我怎么就攤上這事贯莺。” “怎么了宁改?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缕探,是天一觀的道長。 經(jīng)常有香客問我还蹲,道長爹耗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任谜喊,我火速辦了婚禮潭兽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘斗遏。我一直安慰自己山卦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布诵次。 她就那樣靜靜地躺著账蓉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逾一。 梳的紋絲不亂的頭發(fā)上铸本,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音遵堵,去河邊找鬼箱玷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鄙早,可吹牛的內(nèi)容都是我干的汪茧。 我是一名探鬼主播椅亚,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼限番,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了呀舔?” 一聲冷哼從身側(cè)響起弥虐,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤扩灯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后霜瘪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體珠插,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年颖对,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捻撑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缤底,死狀恐怖顾患,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情个唧,我是刑警寧澤江解,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站徙歼,受9級(jí)特大地震影響犁河,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜魄梯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一桨螺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧画恰,春花似錦彭谁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至考润,卻和暖如春狭园,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背糊治。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工唱矛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人井辜。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓绎谦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親粥脚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子窃肠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 4 TreeMap 上一篇,介紹了集合框架中的HashMap對(duì)象刷允,主要講述了HashMap的底層實(shí)現(xiàn)和基本操作冤留。本...
    賈博巖閱讀 121,478評(píng)論 15 98
  • 一纤怒、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對(duì)于byte類型而言...
    龍貓小爺閱讀 4,261評(píng)論 0 16
  • 前言 今天來介紹下TreeMap,TreeMap是基于紅黑樹結(jié)構(gòu)實(shí)現(xiàn)的一種Map糯而,要分析TreeMap的實(shí)現(xiàn)首先就...
    嘟爺MD閱讀 5,080評(píng)論 5 37
  • 一次聚餐,一場酒會(huì)泊窘,說的是幾個(gè)人的故事熄驼,敘的是一代人的感情,這篇文章沒有文采烘豹,也沒有矯糅造作谜洽,他講述的只是個(gè)人以及...
    L追憶H閱讀 337評(píng)論 0 0
  • 我們的故事要從哪里說起呢?是從初次見面時(shí)還是從說的第一句話開始亦或者是從那個(gè)眼神開始吴叶? 學(xué)生時(shí)代的故事大多都令...
    鄭嘉閱讀 511評(píng)論 1 1