成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

關(guān)于ConcurrentHashMap1.8的個(gè)人理解

olle / 3450人閱讀

摘要:數(shù)據(jù)結(jié)構(gòu)重要成員變量代表整個(gè)哈希表??破?,解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制,操作包含三個(gè)操作數(shù)內(nèi)存位置預(yù)期原值和新值。

ConcurrenHashMap 。下面分享一下我對(duì)ConcurrentHashMap 的理解,主要用于個(gè)人備忘。如果有不對(duì),請(qǐng)批評(píng)。

HashMap“嚴(yán)重”的勾起了我對(duì)HashMap家族的好奇心,下面分享一下我對(duì)ConcurrentHashMap 的理解,主要用于個(gè)人備忘。如果有不對(duì),請(qǐng)批評(píng)。

想要解鎖更多新姿勢(shì)?請(qǐng)?jiān)L問https://blog.tengshe789.tech/

總起

HashMap是我們平時(shí)開發(fā)過程中用的比較多的集合,但它是非線程安全的,在涉及到多線程并發(fā)的情況,進(jìn)行g(shù)et操作有可能會(huì)引起死循環(huán),導(dǎo)致CPU利用率接近100%。

因此需要支持線程安全的并發(fā)容器 ConcurrentHashMap 。

數(shù)據(jù)結(jié)構(gòu)

重要成員變量
     /**
     * The array of bins. Lazily initialized upon first insertion.
     * Size is always a power of two. Accessed directly by iterators.
     */
    transient volatile Node[] table;

table代表整個(gè)哈希表。 默認(rèn)為null,初始化發(fā)生在第一次插入操作,默認(rèn)大小為16的數(shù)組,用來存儲(chǔ)Node節(jié)點(diǎn)數(shù)據(jù),擴(kuò)容時(shí)大小總是2的冪次方。

     /**
     * The next table to use; non-null only while resizing.
     */
    private transient volatile Node[] nextTable;

nextTable是一個(gè)連接表,用于哈希表擴(kuò)容,默認(rèn)為null,擴(kuò)容時(shí)新生成的數(shù)組,其大小為原數(shù)組的兩倍。

    /**
     * Base counter value, used mainly when there is no contention,
     * but also as a fallback during table initialization
     * races. Updated via CAS.
     */
    private transient volatile long baseCount;

baseCount保存著整個(gè)哈希表中存儲(chǔ)的所有的結(jié)點(diǎn)的個(gè)數(shù)總和,有點(diǎn)類似于 HashMap 的 size 屬性。 這個(gè)數(shù)通過CAS算法更新

 /**
     * Table initialization and resizing control.  When negative, the
     * table is being initialized or resized: -1 for initialization,
     * else -(1 + the number of active resizing threads).  Otherwise,
     * when table is null, holds the initial table size to use upon
     * creation, or 0 for default. After initialization, holds the
     * next element count value upon which to resize the table.
     */
    private transient volatile int sizeCtl;

初始化哈希表和擴(kuò)容 rehash 的過程,都需要依賴sizeCtl。該屬性有以下幾種取值:

0:默認(rèn)值

-1:代表哈希表正在進(jìn)行初始化

大于0:相當(dāng)于 HashMap 中的 threshold,表示閾值

小于-1:代表有多個(gè)線程正在進(jìn)行擴(kuò)容。(譬如:-N 表示有N-1個(gè)線程正在進(jìn)行擴(kuò)容操作 )

構(gòu)造方法
public ConcurrentHashMap() {
    }
public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));//MAXIMUM_CAPACITY = 1 << 30
        this.sizeCtl = cap;//ConcurrentHashMap在構(gòu)造函數(shù)中只會(huì)初始化sizeCtl值,并不會(huì)直接初始化table,而是延緩到第一次put操作。 
    }
public ConcurrentHashMap(Map m) {
        this.sizeCtl = DEFAULT_CAPACITY;//DEFAULT_CAPACITY = 16
        putAll(m);
    }

構(gòu)造方法是三個(gè)。重點(diǎn)是第二個(gè),帶參的構(gòu)造方法。這個(gè)帶參的構(gòu)造方法會(huì)調(diào)用tableSizeFor()方法,確保table的大小總是2的冪次方(假設(shè)參數(shù)為100,最終會(huì)調(diào)整成256)。算法如下:

/**
     * Returns a power of two table size for the given desired capacity.
     * See Hackers Delight, sec 3.2
     */
    private static final int tableSizeFor(int c) {
        int n = c - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
PUT()方法

put()調(diào)用putVal()方法,讓我們看看:

final V putVal(K key, V value, boolean onlyIfAbsent) {
          //對(duì)傳入的參數(shù)進(jìn)行合法性判斷
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());//計(jì)算鍵所對(duì)應(yīng)的 hash 值
        int binCount = 0;
        for (Node[] tab = table;;) {
            Node f; int n, i, fh;
            //如果哈希表還未初始化,那么初始化它
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
             //根據(jù)hash值計(jì)算出在table里面的位置
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { 
                //如果這個(gè)位置沒有值 ,那么以CAS無鎖式向該位置添加一個(gè)節(jié)點(diǎn)
                if (casTabAt(tab, i, null,
                             new Node(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            //檢測到桶結(jié)點(diǎn)是 ForwardingNode 類型,協(xié)助擴(kuò)容(MOVED  = -1; // hash for forwarding nodes)
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            //桶結(jié)點(diǎn)是普通的結(jié)點(diǎn),鎖住該桶頭結(jié)點(diǎn)并試圖在該鏈表的尾部添加一個(gè)節(jié)點(diǎn)
            else {
                V oldVal = null;
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        //向普通的鏈表中添加元素
                        if (fh >= 0) {
                            binCount = 1;
                            //遍歷鏈表所有的結(jié)點(diǎn)
                            for (Node e = f;; ++binCount) {
                                K ek;
                                //如果hash值和key值相同,則修改對(duì)應(yīng)結(jié)點(diǎn)的value值
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node pred = e;
                                //如果遍歷到了最后一個(gè)結(jié)點(diǎn),那么就證明新的節(jié)點(diǎn)需要插入鏈表尾部
                                if ((e = e.next) == null) {
                                    pred.next = new Node(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        //如果這個(gè)節(jié)點(diǎn)是樹節(jié)點(diǎn),就按照樹的方式插入值
                        else if (f instanceof TreeBin) {
                            Node p;
                            binCount = 2;
                            if ((p = ((TreeBin)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    //如果鏈表長度已經(jīng)達(dá)到臨界值8,就需要把鏈表轉(zhuǎn)換為樹結(jié)構(gòu)(TREEIFY_THRESHOLD = 8)
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
           //CAS 式更新baseCount,并判斷是否需要擴(kuò)容
        addCount(1L, binCount);
        return null;
    }

其實(shí)putVal()也多多少少掉用了其他方法,讓我們繼續(xù)探究一下。

CAS(compare and swap)

科普compare and swap,解決多線程并行情況下使用鎖造成性能損耗的一種機(jī)制,CAS操作包含三個(gè)操作數(shù)——內(nèi)存位置(V)、預(yù)期原值(A)和新值(B)。如果內(nèi)存位置的值與預(yù)期原值相匹配,那么處理器會(huì)自動(dòng)將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會(huì)在CAS指令之前返回該位置的值。CAS有效地說明了“我認(rèn)為位置V應(yīng)該包含值A(chǔ);如果包含該值,則將B放到這個(gè)位置;否則,不要更改該位置,只告訴我這個(gè)位置現(xiàn)在的值即可。

spread

首先,第四行出現(xiàn)的int hash = spread(key.hashCode());這是傳統(tǒng)的計(jì)算hash的方法。key的hash值高16位不變,低16位與高16位異或作為key的最終hash值。(h >>> 16,表示無符號(hào)右移16位,高位補(bǔ)0,任何數(shù)跟0異或都是其本身,因此key的hash值高16位不變。)

 static final int spread(int h) {
        return (h ^ (h >>> 16)) & HASH_BITS;
    }
initTable

第十行, tab = initTable();這個(gè)方法的亮點(diǎn)是,可以讓put并發(fā)執(zhí)行,實(shí)現(xiàn)table只初始化一次 。

initTable()核心思想就是,只允許一個(gè)線程對(duì)表進(jìn)行初始化,如果有其他線程進(jìn)來了,那么會(huì)讓其他線程交出 CPU 等待下次系統(tǒng)調(diào)度。這樣,保證了表同時(shí)只會(huì)被一個(gè)線程初始化。
private final Node[] initTable() {
        Node[] tab; int sc;
        //如果表為空才進(jìn)行初始化操作
        while ((tab = table) == null || tab.length == 0) {
            //如果一個(gè)線程發(fā)現(xiàn)sizeCtl<0,意味著另外的線程執(zhí)行CAS操作成功,當(dāng)前線程只需要讓出cpu時(shí)間片(放棄 CPU 的使用)
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
            //否則說明還未有線程對(duì)表進(jìn)行初始化,那么本線程就來做這個(gè)工作
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        //sc 大于零說明容量已經(jīng)初始化了,否則使用默認(rèn)容量
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node[] nt = (Node[])new Node[n];
                        table = tab = nt;
                        //計(jì)算閾值,等效于 n*0.75
                        sc = n - (n >>> 2);
                    }
                } finally {
                    //設(shè)置閾值
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

接下來,第19行。 tab = helpTransfer(tab, f);這句話。要了解這個(gè),首先需要知道ForwardingNode 這個(gè)節(jié)點(diǎn)類型。它一個(gè)用于連接兩個(gè)table的節(jié)點(diǎn)類。它包含一個(gè)nextTable指針,用于指向下一張hash表。而且這個(gè)節(jié)點(diǎn)的key、value、next指針全部為null,它的hash值為MOVED(static final int MOVED = -1)。

static final class ForwardingNode extends Node {
        final Node[] nextTable;
        ForwardingNode(Node[] tab) {
            super(MOVED, null, null, null);
            this.nextTable = tab;
        }
    //find的方法是從nextTable里進(jìn)行查詢節(jié)點(diǎn),而不是以自身為頭節(jié)點(diǎn)進(jìn)行查找 
    Node find(int h, Object k) {
            // loop to avoid arbitrarily deep recursion on forwarding nodes
            outer: for (Node[] tab = nextTable;;) {
                Node e; int n;
                if (k == null || tab == null || (n = tab.length) == 0 ||
                    (e = tabAt(tab, (n - 1) & h)) == null)
                    return null;
                for (;;) {
                    int eh; K ek;
                    if ((eh = e.hash) == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                    if (eh < 0) {
                        if (e instanceof ForwardingNode) {
                            tab = ((ForwardingNode)e).nextTable;
                            continue outer;
                        }
                        else
                            return e.find(h, k);
                    }
                    if ((e = e.next) == null)
                        return null;
                }
            }
        }
    }
helpTransfer

在擴(kuò)容操作中,我們需要對(duì)每個(gè)桶中的結(jié)點(diǎn)進(jìn)行分離和轉(zhuǎn)移。如果某個(gè)桶結(jié)點(diǎn)中所有節(jié)點(diǎn)都已經(jīng)遷移完成了(已經(jīng)被轉(zhuǎn)移到新表 nextTable 中了),那么會(huì)在原 table 表的該位置掛上一個(gè) ForwardingNode 結(jié)點(diǎn),說明此桶已經(jīng)完成遷移。

helpTransfer什么作用呢?是檢測到當(dāng)前哈希表正在擴(kuò)容,然后讓當(dāng)前線程去協(xié)助擴(kuò)容 !

final Node[] helpTransfer(Node[] tab, Node f) {
        Node[] nextTab; int sc;
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode)f).nextTable) != null) {//新的table,nextTab已經(jīng)存在前提下才能幫助擴(kuò)容
            int rs = resizeStamp(tab.length);//返回一個(gè) 16 位長度的擴(kuò)容校驗(yàn)標(biāo)識(shí)
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {//sizeCtl 如果處于擴(kuò)容狀態(tài)的話
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                  //前 16 位是數(shù)據(jù)校驗(yàn)標(biāo)識(shí),后 16 位是當(dāng)前正在擴(kuò)容的線程總數(shù)
                    //這里判斷校驗(yàn)標(biāo)識(shí)是否相等,如果校驗(yàn)符不等或者擴(kuò)容操作已經(jīng)完成了,直接退出循環(huán),不用協(xié)助它們擴(kuò)容了
                    break;
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {//sc + 1 標(biāo)識(shí)增加了一個(gè)線程進(jìn)行擴(kuò)容
                    transfer(tab, nextTab);//調(diào)用擴(kuò)容方法
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }

helpTransfer精髓的是可以調(diào)用多個(gè)工作線程一起幫助進(jìn)行擴(kuò)容,這樣的效率就會(huì)更高,而不是只有檢查到要擴(kuò)容的那個(gè)線程進(jìn)行擴(kuò)容操作,其他線程就要等待擴(kuò)容操作完成才能工作。

transfer

既然這里涉及到擴(kuò)容的操作,我們也一起來看看擴(kuò)容方法transfer()

private final void transfer(Node[] tab, Node[] nextTab) {
        int n = tab.length, stride;
        //計(jì)算單個(gè)線程允許處理的最少table桶首節(jié)點(diǎn)個(gè)數(shù),不能小于 16
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; // subdivide range
     //剛開始擴(kuò)容,初始化 nextTab 
        if (nextTab == null) {            // initiating
            try {
                @SuppressWarnings("unchecked")
                Node[] nt = (Node[])new Node[n << 1];
                nextTab = nt;
            } catch (Throwable ex) {      // try to cope with OOME
                sizeCtl = Integer.MAX_VALUE;
                return;
            }
            nextTable = nextTab;
            //transferIndex 指向最后一個(gè)桶,方便從后向前遍歷 
            transferIndex = n;
        }
        int nextn = nextTab.length;
           //定義 ForwardingNode 用于標(biāo)記遷移完成的桶
        ForwardingNode fwd = new ForwardingNode(nextTab);
        boolean advance = true;
        boolean finishing = false; // to ensure sweep before committing nextTab
        //i 指向當(dāng)前桶,bound 指向當(dāng)前線程需要處理的桶結(jié)點(diǎn)的區(qū)間下限
        for (int i = 0, bound = 0;;) {
            Node f; int fh;
            //遍歷當(dāng)前線程所分配到的桶結(jié)點(diǎn)
            while (advance) {
                int nextIndex, nextBound;
                if (--i >= bound || finishing)
                    advance = false;
                //transferIndex <= 0 說明已經(jīng)沒有需要遷移的桶了
                else if ((nextIndex = transferIndex) <= 0) {
                    i = -1;
                    advance = false;
                }
                //更新 transferIndex
                   //為當(dāng)前線程分配任務(wù),處理的桶結(jié)點(diǎn)區(qū)間為(nextBound,nextIndex)
                else if (U.compareAndSwapInt
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    bound = nextBound;
                    i = nextIndex - 1;
                    advance = false;
                }
            }
             //當(dāng)前線程所有任務(wù)完成
            if (i < 0 || i >= n || i + n >= nextn) {
                int sc;
                if (finishing) {
                    nextTable = null;
                    table = nextTab;
                    sizeCtl = (n << 1) - (n >>> 1);
                    return;
                }
                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                        return;
                    finishing = advance = true;
                    i = n; // recheck before commit
                }
            }
            //待遷移桶為空,那么在此位置 CAS 添加 ForwardingNode 結(jié)點(diǎn)標(biāo)識(shí)該桶已經(jīng)被處理過了
            else if ((f = tabAt(tab, i)) == null)
                advance = casTabAt(tab, i, null, fwd);
            //如果掃描到 ForwardingNode,說明此桶已經(jīng)被處理過了,跳過即可
            else if ((fh = f.hash) == MOVED)
                advance = true; // already processed
            else {
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        Node ln, hn;
                        //鏈表的遷移操作
                        if (fh >= 0) {
                            int runBit = fh & n;
                            Node lastRun = f;
                            //整個(gè) for 循環(huán)為了找到整個(gè)桶中最后連續(xù)的 fh & n 不變的結(jié)點(diǎn)
                            for (Node p = f.next; p != null; p = p.next) {
                                int b = p.hash & n;
                                if (b != runBit) {
                                    runBit = b;
                                    lastRun = p;
                                }
                            }
                            if (runBit == 0) {
                                ln = lastRun;
                                hn = null;
                            }
                            else {
                                hn = lastRun;
                                ln = null;
                            }
                            for (Node p = f; p != lastRun; p = p.next) {
                                int ph = p.hash; K pk = p.key; V pv = p.val;
                                if ((ph & n) == 0)
                                    ln = new Node(ph, pk, pv, ln);
                                else
                                    hn = new Node(ph, pk, pv, hn);
                            }
                            setTabAt(nextTab, i, ln);
                            setTabAt(nextTab, i + n, hn);
                            setTabAt(tab, i, fwd);
                            advance = true;
                        }
                        //紅黑樹的復(fù)制算法,
                        else if (f instanceof TreeBin) {
                            TreeBin t = (TreeBin)f;
                            TreeNode lo = null, loTail = null;
                            TreeNode hi = null, hiTail = null;
                            int lc = 0, hc = 0;
                            for (Node e = t.first; e != null; e = e.next) {
                                int h = e.hash;
                                TreeNode p = new TreeNode
                                    (h, e.key, e.val, null, null);
                                if ((h & n) == 0) {
                                    if ((p.prev = loTail) == null)
                                        lo = p;
                                    else
                                        loTail.next = p;
                                    loTail = p;
                                    ++lc;
                                }
                                else {
                                    if ((p.prev = hiTail) == null)
                                        hi = p;
                                    else
                                        hiTail.next = p;
                                    hiTail = p;
                                    ++hc;
                                }
                            }
                            ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                                (hc != 0) ? new TreeBin(lo) : t;
                            hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                                (lc != 0) ? new TreeBin(hi) : t;
                            setTabAt(nextTab, i, ln);
                            setTabAt(nextTab, i + n, hn);
                            setTabAt(tab, i, fwd);
                            advance = true;
                        }
                    }
                }
            }
        }
    }

至此,put方法講完了

參考資料~

參考資料

感謝

結(jié)束

此片完了~

想要了解更多精彩新姿勢(shì)?請(qǐng)?jiān)L問我的個(gè)人博客
本篇為原創(chuàng)內(nèi)容,已經(jīng)于07-06在個(gè)人博客率先發(fā)表,隨后CSDN,segmentfault,juejin同步發(fā)出。如有雷同,

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/76697.html

相關(guān)文章

  • 個(gè)人總結(jié):關(guān)于變量提升和函數(shù)提升理解

    摘要:變量提升變量的聲明寫在可以在使用變量之后函數(shù)提升函數(shù)可以先調(diào)用,后聲明上面先解釋了下我理解的這兩個(gè)概念的定義。參考前端基礎(chǔ)進(jìn)階三變量對(duì)象詳解關(guān)于變量提升的理解 變量提升:變量的聲明寫在可以在使用變量之后;函數(shù)提升:函數(shù)可以先調(diào)用,后聲明; 上面先解釋了下我理解的這兩個(gè)概念的定義。要真正理解它們,最好從變量對(duì)象的角度出發(fā)。引出變量對(duì)象的概念,要先理解執(zhí)行上下文,也就是當(dāng)控制器執(zhí)行到可執(zhí)行...

    antz 評(píng)論0 收藏0
  • 關(guān)于閉包個(gè)人理解

    摘要:網(wǎng)上關(guān)于閉包的解釋有很多,大多都過于概念化,定義很精準(zhǔn)也很難看懂在說什么。。首先貼一道經(jīng)典的閉包題理解閉包之前,我們要明確垃圾回收機(jī)制中關(guān)于引用次數(shù)的判斷,即當(dāng)引用對(duì)象的引用計(jì)數(shù)為的時(shí)候,表明此對(duì)象值可回收。 網(wǎng)上關(guān)于閉包的解釋有很多,大多都過于概念化,定義很精準(zhǔn)也很難看懂在說什么。。首先貼一道經(jīng)典的閉包題:` function a(){ var b=0 return function...

    cheng10 評(píng)論0 收藏0
  • 關(guān)于Hashmap個(gè)人理解

    摘要:當(dāng)容量超過容量負(fù)載因子時(shí),進(jìn)行擴(kuò)容操作確定何時(shí)將沖突的鏈表轉(zhuǎn)換成紅黑樹用來確何時(shí)將紅黑樹轉(zhuǎn)換成鏈表當(dāng)鏈表轉(zhuǎn)換成紅黑樹時(shí),需要判斷數(shù)組容量。桶排序核心思想是根據(jù)數(shù)據(jù)規(guī)模劃分,個(gè)相同大小的區(qū)間每個(gè)區(qū)間為一個(gè)桶,桶可理解為容器。 剛剛看到QQ群有人吹Hashmap,一想我啥都不懂,就趕快補(bǔ)了一波。下面分享一下我對(duì)Hashmap的理解,主要用于個(gè)人備忘。如果有不對(duì),請(qǐng)批評(píng)。想要解鎖更多新姿勢(shì)?...

    Yujiaao 評(píng)論0 收藏0
  • 關(guān)于TreeMap個(gè)人理解

    摘要:對(duì)于一棵有效的紅黑樹二叉樹而言我們必須增加如下規(guī)則每個(gè)節(jié)點(diǎn)都只能是紅色或者黑色根節(jié)點(diǎn)是黑色每個(gè)葉節(jié)點(diǎn)節(jié)點(diǎn),空節(jié)點(diǎn)是黑色的。這些約束強(qiáng)制了紅黑樹的關(guān)鍵性質(zhì)從根到葉子的最長的可能路徑不多于最短的可能路徑的兩倍長。 群里的大哥說了,要想懂紅黑樹的應(yīng)用,先要看TreeMap。 想要解鎖更多新姿勢(shì)?請(qǐng)?jiān)L問http://blog.tengshe789.tech/ OK,現(xiàn)在開始: 紅黑樹簡介 紅黑...

    xcc3641 評(píng)論0 收藏0
  • 01_關(guān)于變量個(gè)人理解

    摘要:關(guān)于變量的值的類型的總結(jié)。所以此時(shí)指向新的對(duì)象還是指向被添加了屬性的老對(duì)象, 關(guān)于變量的值的類型的總結(jié)。 //1.當(dāng)多個(gè)變量的值是非引用類型var a=1;var b=a; //系統(tǒng)復(fù)制了a的值并賦值給ba++; //a自身的值被改變,而b的值不受影響 a b的值雖相等但互不影響console.log(a)//2console.log(b)//1 ...

    2shou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<