2, hashmap - treeNode

1, 樹介紹

這里用的是紅黑樹棘幸,比普通的二叉樹多了個(gè)標(biāo)志符,一般的二叉樹結(jié)構(gòu)如下

     a
    /\
   /  \
  b    c

二叉樹有個(gè)缺點(diǎn)倦零,像上面b節(jié)點(diǎn)下如果還有其他多個(gè)節(jié)點(diǎn)误续,c下面沒有,則這個(gè)樹是非平衡的扫茅,非平衡樹的缺點(diǎn)很明顯就是遍歷的層級(jí)可能會(huì)很多蹋嵌,對(duì)效率的提升可能不是很明顯,平衡樹指的的是它的左右兩個(gè)子樹的高度差的絕對(duì)值不超過1
紅黑樹也算是平衡二叉樹的一種葫隙,只是子和父之間的顏色不同栽烂,正常來講應(yīng)該由以下幾點(diǎn)組成

//結(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)
private class Node{
    private Key key;//鍵
    private Value value;//值
    private Node left, right;//指向子樹的鏈接:左子樹和右子樹.
    private int N;//以該節(jié)點(diǎn)為根的子樹中的結(jié)點(diǎn)總數(shù)
    boolean color;//由其父結(jié)點(diǎn)指向它的鏈接的顏色也就是結(jié)點(diǎn)顏色.

1,每個(gè)節(jié)點(diǎn)要么是紅色恋脚,要么是黑色腺办。
2,根節(jié)點(diǎn)必須是黑色
3糟描,紅色節(jié)點(diǎn)不能連續(xù)(也即是怀喉,紅色節(jié)點(diǎn)的孩子和父親都不能是紅色)。
4船响,對(duì)于每個(gè)節(jié)點(diǎn)躬拢,從該點(diǎn)至null(樹尾端)的任何路徑,都含有相同個(gè)數(shù)的黑色節(jié)點(diǎn)灿意。

為何要紅黑樹估灿,可能是和上面的定義有關(guān),在插入數(shù)據(jù)時(shí)缤剧,為了保證樹的平衡型馅袁,會(huì)考慮對(duì)整個(gè)樹進(jìn)行左旋和右旋,如果想系統(tǒng)的學(xué)習(xí)紅黑樹請(qǐng)參考:https://www.cnblogs.com/CarpenterLee/p/5503882.html
這里只說一下和hashmap有關(guān)的內(nèi)容荒辕,下面是treenode的定義

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent;  // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    boolean red;
    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
    }

    /**
     * Returns root of tree containing this node.
     */
    final TreeNode<K,V> root() {
        for (TreeNode<K,V> r = this, p;;) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }

先說繼承部分汗销,LinkedHashMap.Entry<K,V> 最終又繼承了 hashmap 中的 node類犹褒,所以在hashmap中的數(shù)組中的元素可以直接是treenode

static class Entry<K,V> extends HashMap.Node<K,V> {

2, 鏈表轉(zhuǎn)換為紅黑樹

在hashmap 的put方法中有涉及, 第一個(gè)是轉(zhuǎn)換為tree,還有就是在tree中增加元素弛针,另外就是在get方法中從tree中獲取元素

//put方法轉(zhuǎn)換為tree
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    // 如果鏈表長(zhǎng)度大于8叠骑,鏈表轉(zhuǎn)化為紅黑樹,執(zhí)行插入
    treeifyBin(tab, hash);
break;

//put方法中插入元素
else if (p instanceof TreeNode)
    // 如果p是紅黑樹類型削茁,調(diào)用putTreeVal方式賦值
    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

//get方法中獲取元素
if (first instanceof TreeNode)
    return ((TreeNode<K,V>)first).getTreeNode(hash, key);

3, put 方法轉(zhuǎn)換為tree

//入?yún)?為當(dāng)前table宙枷,入?yún)?為當(dāng)前hash,此方法是發(fā)生在鏈表已經(jīng)插入數(shù)據(jù)后茧跋,長(zhǎng)度大于8時(shí)
final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    //如果tab 不存在慰丛,或者長(zhǎng)度小于64 則進(jìn)行擴(kuò)容(暫不知道這種情況何時(shí)發(fā)生)
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        resize();
    //判斷 tab hash下標(biāo) 中的第一個(gè)元素不為null 
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        TreeNode<K,V> hd = null, tl = null;
        do {
            //將普通 node 轉(zhuǎn)為 treeNode
            TreeNode<K,V> p = replacementTreeNode(e, null);
            //第一次 執(zhí)行時(shí)為null,將第一個(gè)元素 賦值給 hd瘾杭,tl表示上一個(gè)诅病,在else里就將所有元素串聯(lián)起來了
            //這里do while 僅僅是完成了treenode 的鏈表鏈接,并沒有轉(zhuǎn)換成一棵樹
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        //將鏈接轉(zhuǎn)為樹
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

上面的方法比較特殊粥烁,僅僅是將普通的node節(jié)點(diǎn)轉(zhuǎn)為treenode的鏈表結(jié)構(gòu)贤笆,并沒有直接轉(zhuǎn)為tree結(jié)構(gòu),接著繼續(xù)看hd.treeify(tab);方法

//方法本身是 treenode 對(duì)象的一個(gè)方法讨阻,下面this 指 tab中 頭對(duì)象
final void treeify(Node<K,V>[] tab) {
    TreeNode<K,V> root = null;
    //從this 開始遍歷芥永,聲明兩個(gè)treenode, x第一次時(shí)是this变勇,this的下一個(gè)next恤左,遍歷的遞增條件就是x=next
    for (TreeNode<K,V> x = this, next; x != null; x = next) {
        //第一次循環(huán)中 next 和root 是同一個(gè),但root作為樹的根節(jié)點(diǎn)搀绣,再后續(xù)的插入元素過程中可能會(huì)因?yàn)樾D(zhuǎn)而發(fā)生改變
        //next 元素就變?yōu)榱随湵?獲取next元素的源頭飞袋,保證鏈表的便利繼續(xù)進(jìn)行
        next = (TreeNode<K,V>)x.next;
        //執(zhí)行時(shí) 將當(dāng)前遍歷的左右樹置空
        x.left = x.right = null;
        //root表示第一個(gè)元素,僅有第一次遍歷時(shí)才會(huì)執(zhí)行這個(gè)方法链患,確定x也就是this是root根節(jié)點(diǎn)巧鸭,red為黑樹
        if (root == null) {
            x.parent = null;
            x.red = false;
            root = x;
        }
        else {
            //拿到當(dāng)前遍歷元素的 key 和 hash
            K k = x.key;
            int h = x.hash;
            Class<?> kc = null;
            //內(nèi)部二次遍歷,也是從root開始遍歷麻捻,從整棵樹的根節(jié)點(diǎn)進(jìn)行大小對(duì)比纲仍,確定擺放位置
            for (TreeNode<K,V> p = root;;) {
                int dir, ph;
                K pk = p.key;
                //如果 當(dāng)前元素hash小于 二次遍歷中的元素時(shí),dir 為-1 贸毕,大于為1郑叠,hash相等暫不考慮
                if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;
                //暫不知道這行的意思,應(yīng)該是hash一樣時(shí)明棍,再對(duì)比key的大小
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0)
                    dir = tieBreakOrder(k, pk);

                TreeNode<K,V> xp = p;
                //二次遍歷中乡革,根據(jù)大小關(guān)系對(duì)比,如果左右位置為null,則直接放置
                //否則會(huì)繼續(xù)遍歷沸版,if語(yǔ)句中嘁傀,p元素已經(jīng)為當(dāng)前遍歷的左右分支,可以繼續(xù)遍歷
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    //因?yàn)閜 在if中已經(jīng)完成了‘next’操作视粮,所以xp就代表二次循環(huán)中的當(dāng)前元素细办,一次遍歷中當(dāng)前元素x 的parent就是xp
                    x.parent = xp;
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    //這個(gè)方法表示,對(duì)當(dāng)前樹進(jìn)行平衡操作蕾殴,并返回新的root笑撞,這個(gè)時(shí)候更新root節(jié)點(diǎn)并不影響一次遍歷中的next
                    //內(nèi)部就是根據(jù)root節(jié)點(diǎn)和剛插入的x元素,來判斷是否平衡
                    root = balanceInsertion(root, x);
                    break;
                }
            }
        }
    }
    //root作為樹的根節(jié)點(diǎn)和最初table中的第一個(gè)元素可能已經(jīng)不是同一個(gè)区宇,確保給定的根是其倉(cāng)的第一個(gè)節(jié)點(diǎn)
    moveRootToFront(tab, root);
}

此方法中還缺少一個(gè)點(diǎn)娃殖,在執(zhí)行此方法時(shí),本身是一個(gè)鏈表存在议谷,現(xiàn)在并沒有將鏈接關(guān)系結(jié)束,這一點(diǎn)可能在 moveRootToFront 或者 balanceInsertion 中有涉及堕虹,本文章主要講解hashmap卧晓,關(guān)于樹的細(xì)節(jié)暫告一段落,如果今后有時(shí)間繼續(xù)研究的話赴捞,會(huì)繼續(xù)更新

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末逼裆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子赦政,更是在濱河造成了極大的恐慌胜宇,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恢着,死亡現(xiàn)場(chǎng)離奇詭異桐愉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掰派,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門从诲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靡羡,你說我怎么就攤上這事系洛。” “怎么了略步?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵描扯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我趟薄,道長(zhǎng)绽诚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮憔购,結(jié)果婚禮上宫峦,老公的妹妹穿的比我還像新娘险领。我一直安慰自己碉考,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布蕴纳。 她就那樣靜靜地躺著屎飘,像睡著了一般妥曲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钦购,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天檐盟,我揣著相機(jī)與錄音,去河邊找鬼押桃。 笑死葵萎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的唱凯。 我是一名探鬼主播羡忘,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼磕昼!你這毒婦竟也來了卷雕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤票从,失蹤者是張志新(化名)和其女友劉穎漫雕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體峰鄙,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浸间,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了先馆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片发框。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖煤墙,靈堂內(nèi)的尸體忽然破棺而出梅惯,到底是詐尸還是另有隱情,我是刑警寧澤仿野,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布铣减,位于F島的核電站,受9級(jí)特大地震影響脚作,放射性物質(zhì)發(fā)生泄漏葫哗。R本人自食惡果不足惜缔刹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劣针。 院中可真熱鬧校镐,春花似錦、人聲如沸捺典。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)襟己。三九已至引谜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間擎浴,已是汗流浹背员咽。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贮预,地道東北人贝室。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仿吞,于是被迫代替她去往敵國(guó)和親档玻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348