最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java8中的HashMap原理詳解

 更新時(shí)間:2023年09月11日 11:59:07   作者:feiyingHiei  
這篇文章主要介紹了java8中的HashMap原理詳解,HashMap是日常開發(fā)中非常常用的容器,HashMap實(shí)現(xiàn)了Map接口,底層的實(shí)現(xiàn)原理是哈希表,HashMap不是一個(gè)線程安全的容器,需要的朋友可以參考下

java8 HashMap實(shí)現(xiàn)原理

HashMap是日常開發(fā)中非常常用的容器,HashMap實(shí)現(xiàn)了Map接口,底層的實(shí)現(xiàn)原理是哈希表,HashMap不是一個(gè)線程安全的容器,jdk8對(duì)HashMap做了一些改進(jìn),作為開發(fā)人員需要對(duì)HashMap的原理有所了解,現(xiàn)在就通過源碼來了解HashMap的實(shí)現(xiàn)原理。

首先看HashMap中的屬性

    //Node數(shù)組
    transient Node<K,V>[] table;
     //當(dāng)前哈希表中k-v對(duì)個(gè)數(shù),實(shí)際就是node的個(gè)數(shù)
    transient int size;
    //修改次數(shù)
    transient int modCount;
    //元素閾值
    int threshold;
    //負(fù)載因子
    final float loadFactor;

這里的threshold = loadFactor * table.length,hash表如果想要保持比較好的性能,數(shù)組的長(zhǎng)度通常要大于元素個(gè)數(shù),默認(rèn)的負(fù)載因子是0.75,用戶可以自行修改,不過最好使用默認(rèn)的負(fù)載因子。

Node是用來存儲(chǔ)KV的節(jié)點(diǎn),每次put(k,v)的時(shí)候就會(huì)包裝成一個(gè)新的Node, Node定義

    static class Node<K,V> implements Map.Entry<K,V> {
        //hash值
        final int hash;
        final K key;
        V value;
        //hash & (capacity - 1) 相同的Node會(huì)形成一個(gè)鏈表
        Node<K,V> next;
        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

put操作

寫入操作是map中最常用的方法,這里看看hashmap的put方法代碼

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

這里先計(jì)算key的hash值,然后調(diào)用putVal()方法,其中hash方法是內(nèi)部自帶的一個(gè)算法,會(huì)對(duì)key的hashcode再做一次hash操作

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

pubVal方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length; //如果數(shù)組為空,先初始化一下
        if ((p = tab[i = (n - 1) & hash]) == null) //如果對(duì)應(yīng)的數(shù)組為空的話,那么就直接new一個(gè)node然后塞進(jìn)去
            tab[i] = newNode(hash, key, value, null);
        else { //如果有值,說明發(fā)生了沖突,那么就先用拉鏈法來處理沖突
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p; //如果頭結(jié)點(diǎn)的key和要插入的key相同,那么就說明找到了之前插入的節(jié)點(diǎn)
            else if (p instanceof TreeNode) //如果鏈表轉(zhuǎn)成了紅黑樹
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) { //如果之前沒有put過這個(gè)節(jié)點(diǎn),那么就new一個(gè)新的節(jié)點(diǎn)
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) 
                        //另外要檢查一下當(dāng)前鏈表的長(zhǎng)度,如果超過8那么就將鏈表轉(zhuǎn)化成紅黑樹
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        //如果找到了之前的節(jié)點(diǎn),那么就跳出
                        break;
                    p = e;
                }
            }
            if (e != null) {
                V oldValue = e.value; 
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e); //在當(dāng)前類中NOOP
                return oldValue;
            }
        }
        ++modCount;
        //如果當(dāng)前元素?cái)?shù)量大于門限值,就要resize整個(gè)hash表,實(shí)際上就是把數(shù)組擴(kuò)大一倍,然后將所有元素重新塞到新的hash表中
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict); //在該類中NOOP
        return null;
    }

在hashtable中默認(rèn)的出現(xiàn)沖突的時(shí)候就會(huì)將沖突的元素形成一個(gè)鏈表,當(dāng)鏈表長(zhǎng)度大于8的時(shí)候就會(huì)將鏈表變成一個(gè)二叉樹,這是java8中做出的改進(jìn),因?yàn)樵谑褂胔ash表的時(shí)候在key特殊的情況下最壞的時(shí)候hash表會(huì)退化成一個(gè)鏈表,那么原有的O(1)的時(shí)間復(fù)雜度就變成了O(n),性能就會(huì)大打折扣,但是引用了紅黑樹之后那么在最好的情況下時(shí)間復(fù)雜度就變成了O(log(n))。

resize方法

final Node<K, V> [] resize() {
......
//去掉了一些代碼,只關(guān)注最核心的node遷移
//resize會(huì)新建一個(gè)數(shù)組,數(shù)組的長(zhǎng)度是原來數(shù)組長(zhǎng)度的兩倍
    for (int j = 0; j < oldCap; ++j) {//遍歷原來的數(shù)組
        Node<K,V> e;
        if ((e = oldTab[j]) != null) {
            oldTab[j] = null;
            if (e.next == null)
                newTab[e.hash & (newCap - 1)] = e; //如果沒有形成鏈表的話,就直接塞到新的hash表中
            else if (e instanceof TreeNode)
                ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); //紅黑樹操作??
            else { // preserve order
                Node<K,V> loHead = null, loTail = null;
                Node<K,V> hiHead = null, hiTail = null;
                Node<K,V> next;
                do {
                    next = e.next;
                    if ((e.hash & oldCap) == 0) { //如果hash值小于oldCap的時(shí)候,那么就還在原來那個(gè)數(shù)組的位置,就把這個(gè)節(jié)點(diǎn)放到low鏈表中
                        if (loTail == null)
                            loHead = e;
                        else
                            loTail.next = e;
                        loTail = e;
                    }
                    else { //否則的話就是因?yàn)閿U(kuò)展數(shù)組長(zhǎng)度,就把原來的節(jié)點(diǎn)放到high鏈表中
                        if (hiTail == null)
                            hiHead = e;
                        else
                            hiTail.next = e;
                        hiTail = e;
                    }
                } while ((e = next) != null);
                if (loTail != null) {
                    loTail.next = null;
                    newTab[j] = loHead; //low鏈表還放在原來的位置
                }
                if (hiTail != null) {
                    hiTail.next = null;
                    newTab[j + oldCap] = hiHead; //high鏈表放到j(luò)+oldCap位置上
                }
            }
        }
    }
}

resize操作就是創(chuàng)建一個(gè)先的數(shù)組,然后把老的數(shù)組中的元素塞到新的數(shù)組中,注意java8中的hashMap中數(shù)組長(zhǎng)度都是2的n次冪,2、4、、8、16….. 這樣的好處就是可以通過與操作來替代求余操作。當(dāng)數(shù)組擴(kuò)大之后,那么每個(gè)元素所在的位置是可以預(yù)期的,就是要不就待在原來的位置,要不就是到j(luò)+oldCap位置上,舉個(gè)栗子,如果原來數(shù)組長(zhǎng)度為4,那么hash為3和7 的元素都會(huì)放在index為3的位置上,當(dāng)數(shù)組長(zhǎng)度變成8的時(shí)候,hash為3的元素還待在index為3的位置,hash為7的元素此時(shí)就要放到index為7的位置上。

resize操作是一個(gè)很重要的操作,resize會(huì)很消耗性能,因此在創(chuàng)建hashMap的時(shí)候最好先預(yù)估容量,防止重復(fù)創(chuàng)建拷貝。

另外hashmap也是非線程安全的,在多線程操作的時(shí)候可能會(huì)產(chǎn)生cpu100%的情況,主要的原因也是因?yàn)樵诙鄠€(gè)線程resize的時(shí)候?qū)е骆湵懋a(chǎn)生了環(huán),這樣下次get操作的時(shí)候就會(huì)容易進(jìn)入死循環(huán)。

get方法()

get的實(shí)現(xiàn)比較簡(jiǎn)單

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) //如果節(jié)點(diǎn)不為空而且頭結(jié)點(diǎn)與查找的key相同就返回
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);//從紅黑樹中查找
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null); //遍歷鏈表查找key相同的node
        }
    }
    return null;
}

到此這篇關(guān)于java8中的HashMap原理詳解的文章就介紹到這了,更多相關(guān)HashMap原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java postgresql數(shù)組字段類型處理方法詳解

    Java postgresql數(shù)組字段類型處理方法詳解

    這篇文章主要介紹了Java postgresql數(shù)組字段類型處理方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法

    MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法

    本文主要介紹了MyBatisPlus唯一索引批量新增或修改的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Java+Selenium設(shè)置元素等待的方法詳解

    Java+Selenium設(shè)置元素等待的方法詳解

    本文主要介紹如何使用java代碼利用Selenium操作瀏覽器,某些網(wǎng)頁(yè)元素加載慢,如何操作元素就會(huì)把找不到元素的異常,此時(shí)需要設(shè)置元素等待,等待元素加載完,再操作,感興趣的可以了解一下
    2023-01-01
  • SpringCloud CircuitBreaker斷路器詳解

    SpringCloud CircuitBreaker斷路器詳解

    Hystrix是一個(gè)用于處理分布式系統(tǒng)延遲和容錯(cuò)的開源庫(kù),而Resilience4J是其后續(xù)的替代品,本文給大家介紹SpringCloud CircuitBreaker斷路器,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • JavaCV?本地視頻推流實(shí)現(xiàn)依賴示例

    JavaCV?本地視頻推流實(shí)現(xiàn)依賴示例

    這篇文章主要為大家介紹了JavaCV?本地視頻推流實(shí)現(xiàn)的依賴示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • RestTemplate對(duì)HttpClient的適配源碼解讀

    RestTemplate對(duì)HttpClient的適配源碼解讀

    這篇文章主要為大家介紹了RestTemplate對(duì)HttpClient的適配源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • SpringBoot整合Mybatis,解決TypeAliases配置失敗的問題

    SpringBoot整合Mybatis,解決TypeAliases配置失敗的問題

    這篇文章主要介紹了SpringBoot整合Mybatis,解決TypeAliases配置失敗的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java 十大排序算法之計(jì)數(shù)排序刨析

    Java 十大排序算法之計(jì)數(shù)排序刨析

    計(jì)數(shù)排序是一個(gè)非基于比較的排序算法,該算法于1954年由 Harold H. Seward 提出。它的優(yōu)勢(shì)在于在對(duì)一定范圍內(nèi)的整數(shù)排序時(shí),它的復(fù)雜度為Ο(n+k)(其中k是整數(shù)的范圍),快于任何比較排序算法
    2021-11-11
  • Java死鎖代碼實(shí)例及產(chǎn)生死鎖必備的四個(gè)條件

    Java死鎖代碼實(shí)例及產(chǎn)生死鎖必備的四個(gè)條件

    這篇文章主要介紹了Java死鎖代碼實(shí)例及產(chǎn)生死鎖必備的四個(gè)條件,Java 發(fā)生死鎖的根本原因是,在申請(qǐng)鎖時(shí)發(fā)生了交叉閉環(huán)申請(qǐng),synchronized在開發(fā)中最好不要嵌套使用,容易導(dǎo)致死鎖,需要的朋友可以參考下
    2024-01-01
  • 詳解springmvc控制登錄用戶session失效后跳轉(zhuǎn)登錄頁(yè)面

    詳解springmvc控制登錄用戶session失效后跳轉(zhuǎn)登錄頁(yè)面

    本篇文章主要介紹了springmvc控制登錄用戶session失效后跳轉(zhuǎn)登錄頁(yè)面,session一旦失效就需要重新登陸,有興趣的同學(xué)可以了解一下。
    2017-01-01

最新評(píng)論

砚山县| 秦皇岛市| 应城市| 荔波县| 漾濞| 顺平县| 高要市| 黔西| 剑阁县| 宁津县| 荥经县| 黄梅县| 瓮安县| 临湘市| 理塘县| 南和县| 乳山市| 栾川县| 专栏| 黄冈市| 土默特左旗| 丰顺县| 宣化县| 盐池县| 洪洞县| 朝阳区| 天镇县| 彰化市| 通辽市| 濮阳县| 阜阳市| 宁乡县| 松江区| 长宁县| 高邑县| 蕉岭县| 丰镇市| 汪清县| 夏邑县| 方城县| 乐至县|