亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

一起讀源碼之 — HashMap(jdk1.8)

VEIGHTZ / 662人閱讀

摘要:的相比的最大的變動就是結(jié)構(gòu)的修改,在之前數(shù)組鏈表的基礎(chǔ)上,增加了紅黑樹的結(jié)構(gòu)。如下圖下面我們就來一起學(xué)習(xí)一下的源碼吧這里我想先吐槽一下的源碼,代碼可讀性比差太多了腦殼疼但是在精簡程度上要比的好一些。

jdk1.8的hashMap相比1.7的最大的變動就是結(jié)構(gòu)的修改,在之前數(shù)組+鏈表的基礎(chǔ)上,增加了紅黑樹的結(jié)構(gòu)。

1.7的hashMap我們已經(jīng)看過了,其中在查找節(jié)點(diǎn)的時候,會去根據(jù)hash找到對應(yīng)的數(shù)組,接著去遍歷之后的鏈表結(jié)構(gòu),當(dāng)hash沖突比較多的時候,鏈表就會非常的長,此時遍歷鏈表的效率就會很低,所以大神們在將紅黑樹加入到了1.8的hashMap中,當(dāng)鏈表長度大于8的時候,會將鏈表轉(zhuǎn)換為紅黑樹,提高了查找節(jié)點(diǎn)的效率。如下圖:

下面我們就來一起學(xué)習(xí)一下jdk1.8的hashMap源碼吧!
這里我想先吐槽一下1.8的源碼,代碼可讀性比1.7差太多了...腦殼疼~但是在精簡程度上要比1.7的好一些。

先看put()方法

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        // 這里p可以視為一個指針,指向tab[i]位置的節(jié)點(diǎn)
        // n: 數(shù)組的length
        // i: 根據(jù)hash算出的數(shù)組下標(biāo)
        Node[] tab; Node p; int n, i;
        // 將tab數(shù)組指向table,并判斷table如果為空,則進(jìn)入resize()中進(jìn)行初始化
        if ((tab = table) == null || (n = tab.length) == 0)
            // 1.8的hashMap將初始化方法和resize()合并到了一起
            n = (tab = resize()).length;
        // 很據(jù)hash值找到tab[i]并將p指向tabl[i],如果沒有內(nèi)容,創(chuàng)建新的鏈表節(jié)點(diǎn)放到i的位置
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, 
        else { // 進(jìn)入else表示tabl[i]處有內(nèi)容,下面需要進(jìn)一步判斷key是否一致
            Node e; K k;
            // 插入的key和tab[i]處的key相等,將p賦值給e(exist)節(jié)點(diǎn)
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 如果該節(jié)點(diǎn)是代表紅黑樹的節(jié)點(diǎn),調(diào)用紅黑樹的插值方法
            else if (p instanceof TreeNode)
                e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
            else { // 進(jìn)入else說明hash相同,且tab[i]處是一個多節(jié)點(diǎn)的鏈表
                // 循環(huán)鏈表
                for (int binCount = 0; ; ++binCount) {
                    // 將e指向p.next,并判斷p.next是否有內(nèi)容,
                    // 如果沒有內(nèi)容,說明tab[i]處沒找到一致的key,將會此節(jié)點(diǎn)作為新節(jié)點(diǎn)插入
                    // 插入的位置為鏈表尾部
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 鏈表節(jié)點(diǎn)數(shù)如果大于8,調(diào)用treeifyBin將鏈表轉(zhuǎn)為紅黑樹
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 
                            treeifyBin(tab, hash);
                        break;
                    }
                    // 在tab[i]中找到了相同的key,跳出循環(huán)
                    // 此時e指向tab[i]中key等于新插入key的鏈表節(jié)點(diǎn)
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            // 插入的key在鏈表中已存在,只需要直接覆蓋即可
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        // 判斷是否需要擴(kuò)容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

還需要注意的是

1.8中的put方法是在鏈表結(jié)尾插入新節(jié)點(diǎn),而1.7是在頭部插入新節(jié)點(diǎn)

1.8是先插入,再擴(kuò)容,1.7是先擴(kuò)容,再插入

接下來看一下resize()擴(kuò)容方法

final Node[] resize() {
        Node[] oldTab = table;
        // 當(dāng)前容量
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        // 當(dāng)前閾值
        int oldThr = threshold;
        int newCap, newThr = 0;
        // 當(dāng)前容量>0,表示map中已有內(nèi)容
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            // 擴(kuò)容一倍,并將閾值×2
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        // 首次put,oldThr > 0 表示使用的是`new HashMap(int initialCapacity)`構(gòu)造器進(jìn)行的初始化
        else if (oldThr > 0) // initial capacity was placed in threshold
            // 初始化大小=閾值
            newCap = oldThr;
        // 首次put,else 表示使用的是默認(rèn)構(gòu)造器`new HashMap()`進(jìn)行的初始化
        else {               // zero initial threshold signifies using defaults
            // 初始化大小=默認(rèn)大小(16)并計(jì)算閾值
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
        // 創(chuàng)建新數(shù)組
        Node[] newTab = (Node[])new Node[newCap];
        table = newTab;
        // 舊數(shù)據(jù)遷移
        if (oldTab != null) {
            // 遍歷舊數(shù)組
            for (int j = 0; j < oldCap; ++j) {
                Node e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    // oldTab[j]處只有一個節(jié)點(diǎn),就不需要遍歷鏈表了,直接將此節(jié)點(diǎn)賦值到新數(shù)組對應(yīng)hash位置上
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    // 處理紅黑樹節(jié)點(diǎn)
                    else if (e instanceof TreeNode)
                        ((TreeNode)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node loHead = null, loTail = null;
                        Node hiHead = null, hiTail = null;
                        Node next;
                        do {
                            next = e.next;
                            // e.hash & oldCap 將舊的鏈表分成了lo(e.hash & oldCap為偶數(shù))和hi(e.hash & oldCap為奇數(shù))兩個鏈
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        // lo鏈會分配到和原下標(biāo)相同的位置
                        // hi鏈會被分配到原下標(biāo)+oldCap的位置
                        if (loTail != null) {
                            loTail.next = null;
                            // lo鏈
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            // hi鏈
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

get() 方法
相對put方法,get就簡單了許多

    final Node getNode(int hash, Object key) {
        Node[] tab; Node first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            // 如果第一個節(jié)點(diǎn)就是需要的,直接返回
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                // 從紅黑樹中取節(jié)點(diǎn)
                if (first instanceof TreeNode)
                    return ((TreeNode)first).getTreeNode(hash, key);
                // 從鏈表中取節(jié)點(diǎn)
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

remove() 刪除節(jié)點(diǎn)

final Node removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node[] tab; Node p; int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {
            // node: 要被刪除的節(jié)點(diǎn)
            Node node = null, e; K k; V v;
            // 如果頭節(jié)點(diǎn)匹配,直接將node指向頭節(jié)點(diǎn)
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                node = p;
            else if ((e = p.next) != null) {
                // 如果頭節(jié)點(diǎn)不匹配,且頭節(jié)點(diǎn)屬于紅黑樹節(jié)點(diǎn),從樹中取出要刪除的節(jié)點(diǎn)
                if (p instanceof TreeNode)
                    node = ((TreeNode)p).getTreeNode(hash, key);
                // 如果頭節(jié)點(diǎn)不匹配,且頭節(jié)點(diǎn)屬于鏈表節(jié)點(diǎn),遍歷鏈表取出要刪除的節(jié)點(diǎn) 
                else {
                    do {
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            //如果找到了符合條件的待刪除節(jié)點(diǎn),根據(jù)節(jié)點(diǎn)類型去紅黑樹中或者鏈表中刪除指定節(jié)點(diǎn)
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)
                    ((TreeNode)node).removeTreeNode(this, tab, movable);
                else if (node == p)
                    tab[index] = node.next;
                else
                    p.next = node.next;
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

總結(jié)一下:

相對1.7的hashmap不同,1.8的結(jié)構(gòu)采用數(shù)組+鏈表+紅黑樹的結(jié)構(gòu)

1.8中的put方法是在鏈表結(jié)尾插入新節(jié)點(diǎn),而1.7是在頭部插入新節(jié)點(diǎn)

1.8是先插入,再擴(kuò)容,1.7是先擴(kuò)容,再插入

至此,1.8的hashMap源碼閱讀到這里就告一段落了,后邊我們會繼續(xù)看一下不同版本的ConcurrentHashMap源碼,歡迎觀看~

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

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/75466.html

相關(guān)文章

  • 集合框架源碼學(xué)習(xí)HashMap(JDK1.8)

    摘要:所謂拉鏈法就是將鏈表和數(shù)組相結(jié)合。若遇到哈希沖突,則將沖突的值加到鏈表中即可。在編寫程序中,要盡量避免。 目錄: 0-1. 簡介 0-2. 內(nèi)部結(jié)構(gòu)分析   0-2-1. JDK18之前   0-2-2. JDK18之后 0-3. LinkedList源碼分析   0-3-1. 構(gòu)造方法   0-3-2. put方法   0-3-3. get方法   0-3-4. resize方法 ...

    yangrd 評論0 收藏0
  • 集合源碼學(xué)習(xí)路---hashMap(jdk1.8)

    摘要:值得位數(shù)有的次方,如果直接拿散列值作為下標(biāo)訪問主數(shù)組的話,只要算法比較均勻,一般是很難出現(xiàn)碰撞的。但是內(nèi)存裝不下這么大的數(shù)組,所以計(jì)算數(shù)組下標(biāo)就采取了一種折中的辦法,就是將得到的散列值與數(shù)組長度做一個與操作。 hashMap簡單介紹 hashMap是面試中的高頻考點(diǎn),或許日常工作中我們只需把hashMap給new出來,調(diào)用put和get方法就完了。但是hashMap給我們提供了一個絕佳...

    kamushin233 評論0 收藏0
  • JDK1.8源碼分析01學(xué)習(xí)建議(可以延伸其他源碼學(xué)習(xí))

    摘要:唐老師,回答道讀源碼是要建立在你的基礎(chǔ)經(jīng)驗(yàn)足夠的情況下。除了自己去閱讀源碼之外,比如學(xué)習(xí)某個類的時候,可以專門結(jié)合一些優(yōu)質(zhì)的博客針對性的對比學(xué)習(xí),并查漏補(bǔ)缺。制定源碼學(xué)習(xí)計(jì)劃。多調(diào)試,跟蹤源碼。如若有好的學(xué)習(xí)方法,可以留言一起交流學(xué)習(xí)。 序言:目前看一看源碼,來提升自己的技術(shù)實(shí)力。同時現(xiàn)在好多面試官都喜歡問源碼,問你是否讀過JDK源碼等等? 針對如何閱讀源碼,也請教了我的老師。下面就先...

    ky0ncheng 評論0 收藏0
  • Java-Mysql你所需要的面試題集內(nèi)容

    摘要:注意排版不需要花花綠綠的,盡量使用語法。協(xié)議的長連接和短連接,實(shí)質(zhì)上是協(xié)議的長連接和短連接。長連接短連接究竟是什么三次握手和四次揮手面試??蜑榱藴?zhǔn)確無誤地把數(shù)據(jù)送達(dá)目標(biāo)處,協(xié)議采用了三次握手策略。 一 簡歷該如何寫 1.1 為什么說簡歷很重要?1.2-這3點(diǎn)你必須知道1.3-兩大法則了解一1.4-項(xiàng)目經(jīng)歷怎么寫?1.5-專業(yè)技能該怎么寫?1.6-開源程序員簡歷模板分享1.7 其他的一些...

    OpenDigg 評論0 收藏0

發(fā)表評論

0條評論

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