HashMap面試題
HashMap面試題
面試題:
- JDK8中的HashMap有哪些改動?
- HashMap的工作原理嗎?
- HashMap中的“死鎖”是怎麼回事?
- HashMap的擴容機制是怎麼樣的?JDK7與JDK8有什麼不同嗎?
- HashMap是執行緒安全的嗎?為什麼?
- HashMap新增的物件為什麼要重寫equals和hashcode?
- JDK8中為什麼要使用紅黑樹?
JDK8中的HashMap有哪些改動
JDK7-HashMap是用位桶+連結串列的形式
JDK8-HashMap是用位桶+連結串列/紅黑樹的形式 (衝突節點數不小於8-1時,轉換成紅黑樹。)
深處的還需要去了解下面這些?
1:什麼是桶排序?
2:紅黑樹
3:get是如何獲取資料
4:擴容機制resize()
HashMap的工作原理嗎?
HashMap的工作原理 :HashMap是基於雜湊法(又稱雜湊法)的原理,使用put(key, value)儲存物件到HashMap中,使用get(key)從HashMap中獲取物件。當我們給put()方法傳遞鍵和值時,我們先對鍵呼叫hashCode()方法,返回的hashCode用於找到bucket(桶)位置來儲存Entry物件。HashMap是在bucket中儲存鍵物件和值物件,作為Map.Entry。並不是僅僅只在bucket中儲存值
HashMap中的“死鎖”是怎麼回事?
HashMap會造成死鎖,因為HashMap是執行緒非安全的,多併發的情況容易造成死鎖,若要高併發推薦使用ConcurrentHashMap;
二個程式T1、T2,HashMap容量為2,T1執行緒放入key A、B、C、D、E。在T1執行緒中A、B、C Hash值相同,於是形成一個連結,假設為A->C->B,而D、E Hash值不同,於是容量不足,需要新建一個更大尺寸的hash表,然後把資料從老的Hash表中
遷移到新的Hash表中(refresh)。這時T2程式闖進來了,T1暫時掛起,T2程式也準備放入新的key,這時也
發現容量不足,也refresh一把。refresh之後原來的連結串列結構假設為C->A,之後T1程式繼續執行,連結結構
為A->C,這時就形成A.next=B,B.next=A的環形連結串列。一旦取值進入這個環形連結串列就會陷入死迴圈。
產生死鎖的根本原因就是在高併發條件下,hashmap擴容的時候產生頭尾相連出現死迴圈而導致的。
解決辦法:
使用ConcurrentHashMap進行替代hash,利用ConcurrentHashMap的執行緒安全,加鎖(sgement)的方式,內部的結構可以讓其在進行寫操作的時候能夠將鎖的粒度
HashMap的擴容機制是怎麼樣的?JDK7與JDK8有什麼不同嗎?
HashMap擴容:
JDK7的時候,Hashmap擴容的時候,當有新的元素進來的時候,他不僅僅會判斷是否大於閾值,還會看當前的陣列位置是否為空,就算這時候已經大於閾值了,但是當前陣列位置為空的時候,他也不會擴容,陣列擴容只有一個辦法:只能把元素存入一個新的陣列。
JDK1.8中在計算新位置的時候並沒有跟1.7中一樣重新進行hash運算,而是用了原位置+原陣列長度這樣一種很巧妙的方式,而這個結果與hash運算得到的結果是一致的,只是會更塊。rehash之後,元素的位置要麼是在原位置,要麼是在原位置再移動2次冪的位置。
JDK1.7 HashMap的結構: Entry陣列+連結串列
JDK1.8 HashMap的結構: Node陣列+連結串列+紅黑樹
儲存原理
根據上面圖片, 我們可以看出, 如果 HashMap 的每個 bucket 裡只有一個 Entry 時,HashMap 可以根據索引、快速地取出該 bucket 裡的 Entry;在發生“Hash 衝突”的情況下,單個 bucket 裡儲存的不是一個 Entry,而是一個 Entry 鏈,系統只能必須按順序遍歷每個 Entry,直到找到想搜尋的 Entry 為止——如果恰好要搜尋的 Entry 位於該 Entry 鏈的最末端(該 Entry 是最早放入該 bucket 中),那系統必須迴圈到最後才能找到該元素。
負載因子和擴容
initailCapacity * loadFactor = HashMap容量
當建立 HashMap 時,有一個預設的負載因子(load factor),其預設值為 0.75,這是時間和空間成本上一種折衷:增大負載因子可以減少 Hash 表(就是那個 Entry 陣列)所佔用的記憶體空間,但會增加查詢資料的時間開銷,而查詢是最頻繁的的操作(HashMap 的 get() 與 put() 方法都要用到查詢);減小負載因子會提高資料查詢的效能,但會增加 Hash 表所佔用的記憶體空間。
如果開始就知道 HashMap 會儲存多個 key-value 對,可以在建立時就使用較大的初始化容量,如果 HashMap 中 Entry 的數量一直不會超過極限容量(capacity * load factor),HashMap 就無需呼叫 resize() 方法重新分配 table 陣列,從而保證較好的效能。
HashMap的大小很簡單,不是實時計算的,而是每次新增加Entry/Node的時候,size就遞增。刪除的時候就遞減。當到達閾值(負載因子*總size)時, 容量翻倍, 並且永遠是2^n, 因為hash取模運算太慢, 2^n的容量可以進行位運算
jdk 1.7 頭插
jdk 1.8+ 尾插
因為hashmap在併發resize時會出現的死迴圈問題, 並且1.7時候用頭插是考慮到了一個所謂的熱點資料的點(新插入的資料可能會更早用到),但這其實是個偽命題, 因為JDK1.7中rehash的時候,舊連結串列遷移新連結串列的時候,如果在新表的陣列索引位置相同,則連結串列元素會倒置(就是因為頭插) 所以最後的結果 還是打亂了插入的順序 所以總的來看支撐1.7使用頭插的這點原因也不足以支撐下去了 所以就乾脆換成尾插 一舉多得
HashMap是執行緒安全的嗎?為什麼?
答:不安全
原因:hashmap在高併發的情況下執行擴容會出現死鎖的問題。
面試官還會問:產生死鎖的原因是什麼?如何解決?採用這種方式將會帶來哪些問題?
死鎖:多執行緒下擴容的時候產生死迴圈,採用ConcurrentHashMap來代替hashmap,帶來的問題是ConcurrentHashMap的效率低,
HashMap新增的物件為什麼要重寫equals和hashcode
在我們的業務系統中判斷物件時有時候需要的不是一種嚴格意義上的相等,而是一種業務上的物件相等。在這種情況下,原生的equals方法就不能滿足我們的需求了
所以這個時候我們需要重寫equals方法,來滿足我們的業務系統上的需求。那麼為什麼在重寫equals方法的時候需要重寫hashCode方法呢?
Object.hashCode的通用約定:
1:在一個應用程式執行期間,如果一個物件的equals方法做比較所用到的資訊沒有被修改的話,那麼,對該物件呼叫hashCode方法多次,它必須始終如一地返回 同一個整數。在同一個應用程式的多次執行過程中,這個整數可以不同,即這個應用程式這次執行返回的整數與下一次執行返回的整數可以不一致。
2:如果兩個物件根據equals(Object)方法是相等的,那麼呼叫這兩個物件中任一個物件的hashCode方法必須產生同樣的整數結果。
3:如果兩個物件根據equals(Object)方法是不相等的,那麼呼叫這兩個物件中任一個物件的hashCode方法,不要求必須產生不同的整數結果。然而,程式設計師應該意識到這樣的事實,對於不相等的物件產生截然不同的整數結果,有可能提高雜湊表(hash table)的效能。
HashSet和HashMap這些基於雜湊值(hash)實現的類。HashMap的底層處理機制是以陣列的方法儲存放入的資料的(Node<K,V>[] table),其中的關鍵是陣列下標的處理。陣列的下標是根據傳入的元素hashCode方法的返回值再和特定的值異或決定的。如果該陣列位置上已經有放入的值了,且傳入的鍵值相等則不處理,若不相等則覆蓋原來的值,如果陣列位置沒有條目,則插入,並加入到相應的連結串列中。檢查鍵是否存在也是根據hashCode值來確定的。所以如果不重寫hashCode的話,可能導致HashSet、HashMap不能正常的運作、
如果我們將某個自定義物件存到HashMap或者HashSet及其類似實現類中的時候,如果該物件的屬性參與了hashCode的計算,那麼就不能修改該物件引數hashCode計算的屬性了。有可能會移除不了元素,導致記憶體洩漏。
總結:
1.new Object(),JVM根據這個物件的Hashcode值,放入到對應的Hash表對應的Key上,如果不同的物件確產生了相同的hash值,也就是發生了Hash key相同導致衝突的情況,那麼就在這個Hash key的地方產生一個連結串列,將所有產生相同hashcode的物件放到這個單連結串列上去,串在一起。
2.比較兩個物件的時候,首先根據他們的hashcode去hash表中找他的物件,當兩個物件的hashcode相同,那麼就是說他們這兩個物件放在Hash表中的同一個key上,那麼他們一定在這個key上的連結串列上。那麼此時就只能根據Object的equal方法來比較這個物件是否equal。當兩個物件的hashcode不同的話,肯定他們不能equals.
JDK8中為什麼要使用紅黑樹
原因:jdk1.8版本後,Java對HashMap做了改進,在連結串列長度大於8的時候,將後面的資料存在紅黑樹中,以加快檢索速度
紅黑樹的特徵:
1:每個節點或者是黑色,或者是紅色。
2:根節點是黑色。
3:每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL或NULL)的葉子節點!]
4:如果一個節點是紅色的,則它的子節點必須是黑色的。
5:從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
紅黑樹的優勢
紅黑樹相比avl樹,在檢索的時候效率其實差不多,都是通過平衡來二分查詢。但對於插入刪除等操作效率提高很多。紅黑樹不像avl樹一樣追求絕對的平衡,他允許區域性很少的不完全平衡,這樣對於效率影響不大,但省去了很多沒有必要的調平衡操作,avl樹調平衡有時候代價較大,所以效率不如紅黑樹,在現在很多地方都是底層都是紅黑樹的天下啦。
紅黑樹的高度只比高度平衡的AVL樹的高度(log2n)僅僅大了一倍,在效能上卻好很多。
總結:
java8不是用紅黑樹來管理hashmap,而是在hash值相同的情況下(且重複數量大於8),用紅黑樹來管理資料。 紅黑樹相當於排序資料,可以自動的使用二分法進行定位,效能較高。一般情況下,hash值做的比較好的話基本上用不到紅黑樹。
紅黑樹犧牲了一些查詢效能 但其本身並不是完全平衡的二叉樹。因此插入刪除操作效率略高於AVL樹。
AVL樹用於自平衡的計算犧牲了插入刪除效能,但是因為最多隻有一層的高度差,查詢效率會高一些。
相關文章
- HashMap常見面試題整理HashMap面試題
- java面試題-HashMap的工作原理Java面試題HashMap
- HashMap:從原始碼分析到面試題HashMap原始碼面試題
- 面試題總結:HashMap實現原理面試題HashMap
- Java面試題:如何對HashMap按鍵值排序Java面試題HashMap排序
- 阿里面試官最喜歡問的21個HashMap面試題阿里HashMap面試題
- 【Java 容器面試題】談談你對HashMap 的理解Java面試題HashMap
- 一道面試題看 HashMap 的儲存方式面試題HashMap
- 面試集錦(十二)hashMap面試HashMap
- hashmap原始碼面試分析HashMap原始碼面試
- Java常見面試真題之中級進階(HashMap篇)Java面試HashMap
- HashMap原始碼分析 —— 一篇文章搞定HashMap面試HashMap原始碼面試
- HashMap?面試?我是誰?我在哪?HashMap面試
- Java集合面試題(03) Java中HashMap和HashTable之間區別Java面試題HashMap
- 面試中HashMap連結串列成環的問題你答出了嗎面試HashMap
- Java面試題:為什麼HashMap不建議使用物件作為Key?Java面試題HashMap物件
- BAT面試必問HashMap原始碼分析BAT面試HashMap原始碼
- HashMap中面試常問的工作原理HashMap面試
- 面試必問:HashMap 底層實現原理分析面試HashMap
- 阿里面試官用HashMap把我問倒了阿里面試HashMap
- 從HashMap面試聊聊網際網路內卷HashMap面試
- 有關 HashMap 面試會問的一切HashMap面試
- 手寫HashMap,快手面試官直呼內行!HashMap面試
- 天下無難試之HashMap面試刁難大全HashMap面試
- 面試必備:HashMap原始碼解析(JDK8)面試HashMap原始碼JDK
- 雜談 —— 面試有感+今日摸魚學習HashMap面試HashMap
- JAVA系列:HashMap常見問題JavaHashMap
- 美團面試題:Hashmap的結構,1.7和1.8有哪些區別(史上最深入的分析)面試題HashMap
- 7000 字說清楚 HashMap,面試點都在裡面了HashMap面試
- Java1.7的HashMap原始碼分析-面試必備技能JavaHashMap原始碼面試
- 害怕面試被問HashMap?這一篇就搞定了!面試HashMap
- 一個HashMap能跟面試官扯上半個小時HashMap面試
- 再也不怕面試官問我JDK8 HashMap了面試JDKHashMap
- 面試時被問到Flutter/Dart的HashMap怎麼辦?面試FlutterDartHashMap
- 手寫一個迷你版 HashMap,面試隨便問!HashMap面試
- 面試題:面試經面試題
- Java集合詳解4:一文讀懂HashMap和HashTable的區別以及常見面試題JavaHashMap面試題
- 「面試題」20+Vue面試題整理面試題Vue