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

資訊專欄INFORMATION COLUMN

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

VEIGHTZ / 618人閱讀

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

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

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

下面我們就來(lái)一起學(xué)習(xí)一下jdk1.8的hashMap源碼吧!
這里我想先吐槽一下1.8的源碼,代碼可讀性比1.7差太多了...腦殼疼~但是在精簡(jiǎn)程度上要比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可以視為一個(gè)指針,指向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],如果沒(mé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說(shuō)明hash相同,且tab[i]處是一個(gè)多節(jié)點(diǎn)的鏈表
                // 循環(huán)鏈表
                for (int binCount = 0; ; ++binCount) {
                    // 將e指向p.next,并判斷p.next是否有內(nèi)容,
                    // 如果沒(méi)有內(nèi)容,說(shuō)明tab[i]處沒(méi)找到一致的key,將會(huì)此節(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)
                    // 此時(shí)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ò)容,再插入

接下來(lái)看一下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]處只有一個(gè)節(jié)點(diǎn),就不需要遍歷鏈表了,直接將此節(jié)點(diǎn)賦值到新數(shù)組對(duì)應(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ù))兩個(gè)鏈
                            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鏈會(huì)分配到和原下標(biāo)相同的位置
                        // hi鏈會(huì)被分配到原下標(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() 方法
相對(duì)put方法,get就簡(jiǎn)單了許多

    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) {
            // 如果第一個(gè)節(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ǎng)h除的節(jié)點(diǎn)
                if (p instanceof TreeNode)
                    node = ((TreeNode)p).getTreeNode(hash, key);
                // 如果頭節(jié)點(diǎn)不匹配,且頭節(jié)點(diǎn)屬于鏈表節(jié)點(diǎn),遍歷鏈表取出要?jiǎng)h除的節(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é)一下:

相對(duì)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源碼閱讀到這里就告一段落了,后邊我們會(huì)繼續(xù)看一下不同版本的ConcurrentHashMap源碼,歡迎觀看~

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

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

相關(guān)文章

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

    摘要:所謂拉鏈法就是將鏈表和數(shù)組相結(jié)合。若遇到哈希沖突,則將沖突的值加到鏈表中即可。在編寫程序中,要盡量避免。 目錄: 0-1. 簡(jiǎn)介 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 評(píng)論0 收藏0
  • 集合源碼學(xué)習(xí)路---hashMap(jdk1.8)

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

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

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

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

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

    OpenDigg 評(píng)論0 收藏0

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

0條評(píng)論

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