国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java 8 HashMap中的TreeNode.putTreeVal方法分析

AJie / 2531人閱讀

摘要:考慮兩大情況,已經(jīng)存在這個紅黑樹中當(dāng)中了,就直接放回對應(yīng)的那個節(jié)點從紅黑樹的節(jié)點開始遍歷,定位到要插入的葉子節(jié)點,插入新節(jié)點除了要維護紅黑樹的平衡外可以參考源碼,還需要維護節(jié)點之間的前后關(guān)系,這里似乎同時是在維護雙向鏈表關(guān)系。

舉例一個入口,利用一個Map構(gòu)造HashMap時

    /**
     * Constructs a new HashMap with the same mappings as the
     * specified Map.  The HashMap is created with
     * default load factor (0.75) and an initial capacity sufficient to
     * hold the mappings in the specified Map.
     *
     * @param   m the map whose mappings are to be placed in this map
     * @throws  NullPointerException if the specified map is null
     */
    public HashMap(Map m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }

然后就是調(diào)用putMapEntries方法,第二個參數(shù)其實可以看作細節(jié),個人認為它和HashMap的子類LinkedHashMap有關(guān),evict是逐出的意思,如果基于LinkedHashMap實現(xiàn)LRU緩存的話,這個evict參數(shù)正好就用上了。

    /**
     * Implements Map.putAll and Map constructor
     *
     * @param m the map
     * @param evict false when initially constructing this map, else
     * true (relayed to method afterNodeInsertion).
     */
    final void putMapEntries(Map m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

可以看到在for循環(huán)中遍歷舊的entrySet視圖,然后將一個個的key-value對放入新構(gòu)造的HashMap中,

            for (Map.Entry e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }

展開putVal(hash(key), key, value, false, evict);

    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent if true, don"t change existing value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node[] tab; Node p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

通過hash(key)定位到HashMap中tab數(shù)組的索引,如果這個數(shù)組元素的頭節(jié)點正好是TreeNode類型,那么就將執(zhí)行

e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

此時this是HashMap自身。
putTreeVal考慮兩大情況,
1)key已經(jīng)存在這個紅黑樹中當(dāng)中了,就直接放回對應(yīng)的那個節(jié)點;
2)從紅黑樹的root節(jié)點開始遍歷,定位到要插入的葉子節(jié)點,插入新節(jié)點;
putTreeVal除了要維護紅黑樹的平衡外(可以參考TreeMap源碼),還需要維護節(jié)點之間的前后關(guān)系,這里似乎同時是在維護雙向鏈表關(guān)系。

        /**
         * Tree version of putVal.
         */
        final TreeNode putTreeVal(HashMap map, Node[] tab,
                                       int h, K k, V v) {
            Class kc = null;
            boolean searched = false;
            TreeNode root = (parent != null) ? root() : this;
            for (TreeNode p = root;;) {
                int dir, ph; K pk;
                if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;
                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                    return p;
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) {
                    if (!searched) {
                        TreeNode q, ch;
                        searched = true;
                        if (((ch = p.left) != null &&
                             (q = ch.find(h, k, kc)) != null) ||
                            ((ch = p.right) != null &&
                             (q = ch.find(h, k, kc)) != null))
                            return q;
                    }
                    dir = tieBreakOrder(k, pk);
                }

                TreeNode xp = p;
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    Node xpn = xp.next;
                    TreeNode x = map.newTreeNode(h, k, v, xpn);
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    xp.next = x;
                    x.parent = x.prev = xp;
                    if (xpn != null)
                        ((TreeNode)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x));
                    return null;
                }
            }
        }

下面重點分析putTreeVal方法
1 首先找到root節(jié)點,

TreeNode root = (parent != null) ? root() : this;

這里的this是指TreeNode自己,從某個節(jié)點一直往上溯,直到parent==null的情況
2 遞歸遍歷root
判斷節(jié)點之間的hash大小,如果hash值相等采用key比較等
然后采用左子樹或者右子樹,繼續(xù)遍歷
(關(guān)于key值大小的比較算是細節(jié)的地方,這里暫且代入String類型的key解讀源碼以圖整體思路流暢)
3 如果遍歷到了葉子節(jié)點
比如上一步采用左子樹,而左子樹剛好是葉子節(jié)點,p == null
此時遞歸遍歷結(jié)束

                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    Node xpn = xp.next;
                    TreeNode x = map.newTreeNode(h, k, v, xpn);
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    xp.next = x;
                    x.parent = x.prev = xp;
                    if (xpn != null)
                        ((TreeNode)xpn).prev = x;
                    moveRootToFront(tab, balanceInsertion(root, x));
                    return null;
                }

xp是葉子節(jié)點的父節(jié)點,這個節(jié)點不是null,葉子節(jié)點p一定是null
新增一個節(jié)點x,next指向原來父節(jié)點的.next,x就是新增的葉子節(jié)點
1) 處理紅黑樹的關(guān)系
父節(jié)點xp和葉子節(jié)點x的關(guān)系,落在左子樹還是右子樹;
x的parent指向父節(jié)點xp x.parent = xp
最后保持紅黑樹平衡
2)處理雙向鏈表的關(guān)系
類似于在xp-->xpn(xp.next)中間插入新的節(jié)點x,

x = map.newTreeNode(h, k, v, xpn);
x.prev = xp
//如果xpn不是null,則處理xpn的prev
((TreeNode)xpn).prev = x;

圖示

3) 保持紅黑樹平衡

moveRootToFront(tab, balanceInsertion(root, x));

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/71407.html

相關(guān)文章

  • [學(xué)習(xí)筆記-Java集合-4] Map - HashMap源碼分析

    摘要:存儲結(jié)構(gòu)在中,的實現(xiàn)采用了數(shù)組鏈表紅黑樹的復(fù)雜結(jié)構(gòu),數(shù)組的一個元素又稱作桶。當(dāng)一個鏈表的元素個數(shù)達到一定的數(shù)量且數(shù)組的長度達到一定的長度后,則把鏈表轉(zhuǎn)化為紅黑樹,從而提高效率。 簡介 HashMap采用key/value存儲結(jié)構(gòu),每個key對應(yīng)唯一的value,查詢和修改的速度都很快,能達到O(1)的平均時間復(fù)雜度。它是非線程安全的,且不保證元素存儲的順序; 繼承體系 showImg(...

    wing324 評論0 收藏0
  • java源碼一帶一路系列】之HashMap.putVal()

    摘要:表示該類本身不可比表示與對應(yīng)的之間不可比。當(dāng)數(shù)目滿足時,鏈表將轉(zhuǎn)為紅黑樹結(jié)構(gòu),否則繼續(xù)擴容。至此,插入告一段落。當(dāng)超出時,哈希表將會即內(nèi)部數(shù)據(jù)結(jié)構(gòu)重建至大約兩倍。要注意的是使用許多有這相同的鍵值肯定會降低哈希表性能。 回顧上期?觀光線路圖:putAll() --> putMapEntries() --> tableSizeFor() --> resize() --> hash() --...

    cloud 評論0 收藏0
  • JDK源碼那些事兒之并發(fā)ConcurrentHashMap上篇

    前面已經(jīng)說明了HashMap以及紅黑樹的一些基本知識,對JDK8的HashMap也有了一定的了解,本篇就開始看看并發(fā)包下的ConcurrentHashMap,說實話,還是比較復(fù)雜的,筆者在這里也不會過多深入,源碼層次上了解一些主要流程即可,清楚多線程環(huán)境下整個Map的運作過程就算是很大進步了,更細的底層部分需要時間和精力來研究,暫不深入 前言 jdk版本:1.8 JDK7中,ConcurrentH...

    Leck1e 評論0 收藏0
  • HashMap源碼分析(一):JDK源碼分析系列

    摘要:當(dāng)一個值中要存儲到的時候會根據(jù)的值來計算出他的,通過哈希來確認到數(shù)組的位置,如果發(fā)生哈希碰撞就以鏈表的形式存儲在源碼分析中解釋過,但是這樣如果鏈表過長來的話,會把這個鏈表轉(zhuǎn)換成紅黑樹來存儲。 正文開始 注:JDK版本為1.8 HashMap1.8和1.8之前的源碼差別很大 目錄 簡介 數(shù)據(jù)結(jié)構(gòu) 類結(jié)構(gòu) 屬性 構(gòu)造方法 增加 刪除 修改 總結(jié) 1.HashMap簡介 H...

    wdzgege 評論0 收藏0
  • java源碼

    摘要:集合源碼解析回歸基礎(chǔ),集合源碼解析系列,持續(xù)更新和源碼分析與是兩個常用的操作字符串的類。這里我們從源碼看下不同狀態(tài)都是怎么處理的。 Java 集合深入理解:ArrayList 回歸基礎(chǔ),Java 集合深入理解系列,持續(xù)更新~ JVM 源碼分析之 System.currentTimeMillis 及 nanoTime 原理詳解 JVM 源碼分析之 System.currentTimeMi...

    Freeman 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<