国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析

這篇具有很好參考價值的文章主要介紹了Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

前言

List、Set、HashMap作為Java中常用的集合,需要深入認(rèn)識其原理和特性。

本篇博客介紹常見的關(guān)于Java中HashMap集合的面試問題,結(jié)合源碼分析題目背后的知識點。

關(guān)于List的博客文章如下:

  • Java進(jìn)階(List)——面試時List常見問題解讀 & 結(jié)合源碼分析

關(guān)于的Set的博客文章如下:

  • Java進(jìn)階(Set)——面試時Set常見問題解讀 & 結(jié)合源碼分析

其他關(guān)于HaseMap的文章如下:

  • Java學(xué)數(shù)據(jù)結(jié)構(gòu)(3)——樹Tree & B樹 & 紅黑樹 & Java標(biāo)準(zhǔn)庫中的集合Set與映射Map & 使用多個映射Map的案例
  • Java學(xué)數(shù)據(jù)結(jié)構(gòu)(4)——散列表Hash table & 散列函數(shù) & 哈希沖突

引出


1.jdk1.7 HashMap:數(shù)組+單向鏈表;
2.jdk1.8 HashMap:數(shù)組+鏈表(單向)+紅黑樹;
3.當(dāng)鏈表節(jié)點的數(shù)量達(dá)到8個時,通過treeify轉(zhuǎn)為紅黑樹;
4.首次添加元素,初始容量16,大于16時,雙倍擴(kuò)容;
5.HashMap設(shè)置長度,第一個2的冪次方的值;
6.紅黑樹元素的高位或者低位節(jié)點個數(shù)<6時,那么就調(diào)用untreeify方法來退回鏈表結(jié)構(gòu);
7.jdk1.7采用的是頭插法,即新來元素在鏈表起始的位置,而jdk1.8采用尾插法,可以有效的避免在多線程操作中產(chǎn)生死循環(huán);
8.ConcurrentHashMap高并發(fā)線程安全;

核心:鍵值對,KEY不可重復(fù),VALUE可以重復(fù)

HashMap底層結(jié)構(gòu)是什么?

JDK 1.7和1.8 對比

jdk1.7 HashMap:數(shù)組+單向鏈表

jdk1.8 HashMap:數(shù)組+鏈表(單向)+紅黑樹

源碼Node類

源碼可以看到HashMap內(nèi)部定義了靜態(tài)Node類,Node類中成員有

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

Node<K,V> next;

同樣可以看到HashMap內(nèi)部定義了靜態(tài)TreeNode類,TreeNode類中成員有

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

TreeNode<K,V> left;
TreeNode<K,V> right;

可以看出存在紅黑樹

而TreeNode繼承了 LinkedHashMap.Entry,點進(jìn)查看,可以看到 Entry也繼承了 HashMap.Node。所以,TreeNode紅黑樹是從鏈表Node中轉(zhuǎn)換過來的

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

整體圖:

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

HashMap如何解決Hash碰撞問題?HashMap 何時從單鏈表,轉(zhuǎn)換為紅黑樹?

  1. 首先理解什么是hash碰撞問題,HashMap存放的元素的KEY需要經(jīng)過hash計算得到hash值,而這個hash值可以理解為就是此元素要存放的位置,即數(shù)組中的下標(biāo);但是如果兩個不同元素經(jīng)過hash計算后,得到的hash值相同時,即兩個元素要存放的位置為同一個位置,產(chǎn)生沖突,這種現(xiàn)象就叫做hash碰撞。
  2. 要想了解HashMap是如何解決hash碰撞的,那我們就需要看看HashMap的put方法的源碼中的核心操作

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
     //添加第一個元素時,會進(jìn)入這個if結(jié)構(gòu),table為null,則第一次初始化這個table數(shù)組的長度為16
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
     //判斷添加的元素的KEY經(jīng)過hash計算得到的下標(biāo)位置是否為null
        if ((p = tab[i = (n - 1) & hash]) == null)
            //如果是null,則直接添加元素
            tab[i] = newNode(hash, key, value, null);
     //不為null的情況
        else {
            Node<K,V> e; K k;
            //如果key相同,則用新的元素添加到舊元素的位置,后續(xù)會將就元素返回
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //判斷是否為紅黑樹節(jié)點,如果是紅黑樹節(jié)點,則添加為紅黑樹
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            //鏈表類型
            else {
                //通過for循環(huán)遍歷鏈表節(jié)點
                for (int binCount = 0; ; ++binCount) {
                    //如果鏈表節(jié)點next節(jié)點為空
                    if ((e = p.next) == null) {
                        //則添加至鏈表的next節(jié)點屬性中
                        p.next = newNode(hash, key, value, null);
                        //如果鏈表節(jié)點 >= 7 說明鏈表存在8個已存的元素節(jié)點
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //轉(zhuǎn)為紅黑樹方法
                            treeifyBin(tab, hash);
                        break;
                    }
                    //如果KEY相同,匹配其他API 如 putIfAbsent()
                    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;
            }
 ....

當(dāng)鏈表節(jié)點的數(shù)量達(dá)到8個時,通過treeify轉(zhuǎn)為紅黑樹

?

總結(jié):HashMap是通過3層 if 結(jié)構(gòu)來判斷,數(shù)組下標(biāo)位置是否有元素和下標(biāo)位置的類型是鏈表還是紅黑樹,然后通過鏈表和紅黑樹來解決hash碰撞的問題,當(dāng)鏈表節(jié)點>=7時(當(dāng)鏈表節(jié)點的數(shù)量達(dá)到8個時),會通過treeify轉(zhuǎn)為紅黑樹。

HashMap的擴(kuò)容機(jī)制?HashMap的數(shù)組何時需要擴(kuò)容?

1.首次添加元素

HashMap在第一次添加元素時,會進(jìn)入第一個if結(jié)構(gòu)來初始化數(shù)組的長度

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

2.初始容量16

//添加第一個元素時,會進(jìn)入這個if結(jié)構(gòu),table為null,則第一次初始化這個table數(shù)組的長度為16
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

3.大于16時,雙倍擴(kuò)容

此處resize方法就是擴(kuò)容方法,jdk8中,resize方法除了擴(kuò)容還增加了初始化的功能,進(jìn)入此方法我們可以看一下源碼

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

 final Node<K,V>[] resize() {
     Node<K,V>[] oldTab = table;
     int oldCap = (oldTab == null) ? 0 : oldTab.length;
     int oldThr = threshold;
     int newCap, newThr = 0;
     if (oldCap > 0) {
         //如果當(dāng)前數(shù)組的長度>=最大值(2^30)時,將預(yù)值threshold設(shè)置為最大值
         if (oldCap >= MAXIMUM_CAPACITY) {
             threshold = Integer.MAX_VALUE;
             return oldTab;
         }
         //如果當(dāng)前數(shù)組的長度>=默認(rèn)的初始長度16,則雙倍擴(kuò)容
         else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                  oldCap >= DEFAULT_INITIAL_CAPACITY)
             newThr = oldThr << 1; // double threshold
     }
     ...

調(diào)用resize方法的地方

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

      final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {    
          ...
              ++modCount;
          if (++size > threshold)
              resize();
          afterNodeInsertion(evict);
          return null;

總結(jié)分析

  1. 從上面可以看出,HashMap在完成put元素存儲后,會判斷++size是否>了閾值,如果是就會去擴(kuò)容
  2. 下面這個方法是在,put元素為鏈表節(jié)點,并且要轉(zhuǎn)為紅黑樹時,會調(diào)用該方法,該方法會在一開始就判斷是否需要擴(kuò)容

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

 final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
  1. 判斷擴(kuò)容的核心就是threshold這個值

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

  1. 從resize方法中看到,HashMap在擴(kuò)容時,是之前的雙倍擴(kuò)容

HashMap設(shè)置長度為11,那么數(shù)組的容量為多少?

第一個2的冪次方的值

指定了長度初始化HashMap時,它會將數(shù)組的容量經(jīng)過一系列算法,設(shè)置為大于我們自定義值的第一個2的冪次方的值,

即 設(shè)置為11 , 則為2^4=16

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

 static final int tableSizeFor(int cap) {
        int n = cap - 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;
    }

HashMap 何時從紅黑樹轉(zhuǎn)換為 單鏈模式?

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

  1. HashMap在resize方法執(zhí)行時,會將元素從舊數(shù)組轉(zhuǎn)入新數(shù)組,此時如果轉(zhuǎn)移元素為紅黑樹結(jié)構(gòu),那么就會調(diào)用split方法來分割紅黑樹方便轉(zhuǎn)移
  2. split方法內(nèi)部,在分割時,會生成高位樹與低位樹兩種此時也會進(jìn)行判斷如果紅黑樹元素的高位或者低位節(jié)點個數(shù)<6時,那么就調(diào)用untreeify方法來退回鏈表結(jié)構(gòu)

split方法和untreeify方法

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
    ...        //lowhead   低位樹
        if (loHead != null) {
            //在紅黑樹節(jié)點元素往新數(shù)組中添加時,會調(diào)用split方法來重組這個紅黑樹
            //此時會判斷,紅黑樹的節(jié)點操作次數(shù)是否<6,即low樹(低位樹的節(jié)點數(shù))< 6時,會通過untreeify方法來退化為鏈表
            if (lc <= UNTREEIFY_THRESHOLD)
                tab[index] = loHead.untreeify(map);
            else {
                tab[index] = loHead;
                if (hiHead != null) // (else is already treeified)
                    loHead.treeify(tab);
            }
        }
        //此時會判斷,紅黑樹的節(jié)點操作次數(shù)是否<6,即high樹(高位樹的節(jié)點數(shù))< 6時,會通過untreeify方法來退化為鏈表
        //highhead  高位樹 
        if (hiHead != null) {
            if (hc <= UNTREEIFY_THRESHOLD)
                tab[index + bit] = hiHead.untreeify(map);
            else {
                tab[index + bit] = hiHead;
                if (loHead != null)
                    hiHead.treeify(tab);
            }
        }
}

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

HashMap 為什么在多線程并發(fā)使用過程中,容易造成死循環(huán)/死鎖?

圖示:

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

  1. 產(chǎn)生死循環(huán)的核心原因是因為,jdk1.7采用的是頭插法,即新來元素在鏈表起始的位置,而jdk1.8采用尾插法,可以有效的避免在多線程操作中產(chǎn)生以上死循環(huán)
  2. 但是HashMap不是線程安全的,所以在多線程的場景中,雖然不會出現(xiàn)死鎖/死循環(huán),但是還是會出現(xiàn)節(jié)點丟失的情況,所以在并發(fā)的場景中推薦使用ConcurrentHashMap

如何得到一個線程安全的HashMap集合?

Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析,Java,java,面試,開發(fā)語言

ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap<>();

總結(jié)

1.jdk1.7 HashMap:數(shù)組+單向鏈表;
2.jdk1.8 HashMap:數(shù)組+鏈表(單向)+紅黑樹;
3.當(dāng)鏈表節(jié)點的數(shù)量達(dá)到8個時,通過treeify轉(zhuǎn)為紅黑樹;
4.首次添加元素,初始容量16,大于16時,雙倍擴(kuò)容;
5.HashMap設(shè)置長度,第一個2的冪次方的值;
6.紅黑樹元素的高位或者低位節(jié)點個數(shù)<6時,那么就調(diào)用untreeify方法來退回鏈表結(jié)構(gòu);
7.jdk1.7采用的是頭插法,即新來元素在鏈表起始的位置,而jdk1.8采用尾插法,可以有效的避免在多線程操作中產(chǎn)生死循環(huán);
8.ConcurrentHashMap高并發(fā)線程安全;文章來源地址http://www.zghlxwxcb.cn/news/detail-715422.html

到了這里,關(guān)于Java進(jìn)階(HashMap)——面試時HashMap常見問題解讀 & 結(jié)合源碼分析的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • JavaEE 面試常見問題

    JavaEE 面試常見問題

    Mybatis 是一種典型的半自動的 ORM 框架,所謂的半自動,是因為還需要手動的寫 SQL 語句,再由框架根據(jù) SQL 及 傳入數(shù)據(jù)來組裝為要執(zhí)行的 SQL 。其優(yōu)點為: 1. 因為由程序員自己寫 SQL ,相對來說學(xué)習(xí)門檻更低,更容易入門。 2. 更方便做 SQL 的性能優(yōu)化及維護(hù)。 3. 對關(guān)系型數(shù)據(jù)

    2024年02月14日
    瀏覽(36)
  • 面試-Dubbo常見問題

    Dubbo 是一個RPC框架,包含注冊中心,服務(wù)提供方,服務(wù)消費方,控制臺,監(jiān)控中心。 Dubbo 啟動時會從注冊中心拉取消費者需要的提供方信息,如果依賴的服務(wù)提供方不可用,Dubbo消費方會啟動失敗,并且不停的向注冊中心請求提供方信息,拋出異常找不到對應(yīng)的提供方??梢?/p>

    2024年02月08日
    瀏覽(24)
  • 項目經(jīng)理崗面試常見問題

    一、注意事項 ? ·電面邀約確認(rèn)(避免hr刷KPI): 請問貴司招聘的是什么崗位,是新建團(tuán)隊還是原有團(tuán)隊? 這邊面試流程是怎樣的,是 leader 直接面,還是? ? ·面試前鋪墊: 如果您對某部分感興趣,請隨時打斷我。 ? ·面試中發(fā)揮: 盡量采用 STAR 原則回答,即 情境( Si

    2024年02月05日
    瀏覽(30)
  • JVM基礎(chǔ),面試常見問題

    JVM基礎(chǔ),面試常見問題

    目錄 一.運行時數(shù)據(jù)區(qū)域 1.線程獨享 (1)棧 (2)程序計數(shù)器 2.線程共享 (1)方法區(qū) (2)堆 二.內(nèi)存如何分配 1.指針碰撞法 2.空閑列表法 3.TLAB 三.對象在內(nèi)存中的組成 1.對象頭 (1)markword (2)指向類型的指針 (3)如果是數(shù)組-》數(shù)組長度 2.實例數(shù)據(jù) 3.對齊填充 四.如何訪

    2024年01月23日
    瀏覽(21)
  • 【數(shù)據(jù)結(jié)構(gòu)面試常見問題】

    【數(shù)據(jù)結(jié)構(gòu)面試常見問題】

    數(shù)據(jù)結(jié)構(gòu)作為計算機(jī)的一門基礎(chǔ)學(xué)科,它在面試中占有很大的比重,本科階段,我們也學(xué)過數(shù)據(jù)結(jié)構(gòu)與算法,內(nèi)容比較多,也比較難,尤其是圖的應(yīng)用以及各類查找和排序算法,這些也都是核心內(nèi)容。數(shù)據(jù)結(jié)構(gòu)在實際的應(yīng)用中也比較多,因此,整理一些常見的筆試、面試的數(shù)

    2024年03月22日
    瀏覽(15)
  • 單片機(jī)面試常見問題

    1、中斷的概念?簡述中斷的過程 (1)中斷:指CPU在正常執(zhí)行程序的過程中,由于內(nèi)部/外部事件的觸發(fā)或由程序的預(yù)先安排,引起CPU暫時中斷當(dāng)前正在運行的程序,轉(zhuǎn)而執(zhí)行為內(nèi)部/外部事件或程序預(yù)先安排的事件的服務(wù)子程序,待中斷服務(wù)子程序執(zhí)行完畢后,CPU再返回到被

    2024年04月10日
    瀏覽(23)
  • docker常見面試問題詳解

    docker常見面試問題詳解

    在面試的時候,面試官常常會問一些問題: docker是什么,能做什么? docker和虛擬機(jī)的區(qū)別是什么呢? docker是用什么做隔離的? docke的網(wǎng)絡(luò)類型?docker數(shù)據(jù)之間是如何通信的? docker的數(shù)據(jù)保存問題? 常用的docker命令? docker制作鏡像相關(guān)? 下面,就讓我來詳細(xì)說明一些這些問

    2024年02月10日
    瀏覽(31)
  • kubernetes常見面試問題詳解

    kubernetes常見面試問題詳解

    在面試的時候,面試官常常會問一些問題: k8s是什么?有什么用? k8s由哪些組件組成? pod的啟動流程? k8s里有哪些控制器? k8s的調(diào)度器里有哪些調(diào)度算法? pod和pod之間的通信過程? 外面用戶訪問pod數(shù)據(jù)流程? 你常用哪些命令? 容器的類型? 3種探針? pod的升級? HPA、V

    2024年02月10日
    瀏覽(21)
  • 大數(shù)據(jù)常見面試問題匯總

    目錄 第1章 核心技術(shù) 1.1 LinuxShell 1.1.1 Linux常用高級命令 1.1.2 Shell常用工具及寫過的腳本 1.1.3 Shell中單引號和雙引號區(qū)別 1.2 Hadoop 1.2.1 Hadoop常用端口號 1.2.2 HDFS讀流程和寫流程 1.2.3 HDFS小文件處理 1.2.4 HDFS的NameNode內(nèi)存 1.2.5 Shuffle及優(yōu)化 1.2.6 Yarn工作機(jī)制 1.2.7 Yarn調(diào)度器 1.2.8 HDFS塊大

    2024年02月14日
    瀏覽(21)
  • List常見面試問題

    List常見面試問題

    Java中的List是一種存放有序的、可以重復(fù)的數(shù)據(jù)的集合,它允許重復(fù)元素的存在。List中的元素都有對應(yīng)的一個序列號(索引)記錄著元素的位置,因此可以通過這個序列號來訪問元素。 ? Java中的List有三種實現(xiàn)方式:ArrayList、LinkedList和Vector。其中,ArrayList是基于數(shù)組實現(xiàn)的,

    2024年02月09日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包