一篇文章帶你吃透hashmap(面試指南升級版)
威力加強版已更新,大家可以移步:一文讀懂JDK1.7,JDK1.8,JDK1.9的hashmap,hashtable,conc… – 簡書
1:hashmap簡介(如下,陣列-連結串列形式)
HashMap的儲存結構
圖中,紫色部分即代表雜湊表,也稱為雜湊陣列(預設陣列大小是16,每對key-value鍵值對其實是存在map的內部類entry裡的),陣列的每個元素都是一個單連結串列的頭節點,跟著的綠色連結串列是用來解決衝突的,如果不同的key對映到了陣列的同一位置處,就將其放入單連結串列中。
2:hashmap原理(即put和get原理)
2.1 put原理
1.根據key獲取對應hash值:int hash = hash(key.hash.hashcode())
2.根據hash值和陣列長度確定對應陣列引int i = indexFor(hash, table.length); 簡單理解就是i = hash值%模以 陣列長度(其實是按位與運算)。如果不同的key都對映到了陣列的同一位置處,就將其放入單連結串列中。且新來的是放在頭節點。
2.2 get原理
1.通過hash獲得對應陣列位置,遍歷該陣列所在連結串列(key.equals())
3:hashcode相同,衝突怎麼辦?
“頭插法”,放到對應的連結串列的頭部。
3.1:為什麼是頭插法(其設計原理是什麼)?
因為HashMap的發明者認為,後插入的Entry被查詢的可能性更大(因為get查詢的時候會遍歷整個連結串列)。
4.hashmap的預設陣列長度是多少?
預設為16
4.1 為什麼?
之所以選擇16,是為了服務於從key對映到index的hash演算法(看下面)。
5:hashmap達到預設負載因子(0.75)怎麼辦?
自動雙倍擴容,擴容後重新計算每個鍵值對位置。且長度必須為16或者2的冪次
5.1為啥要16或者2的冪次?
若不是16或者2的冪次,位運算的結果不夠均勻分佈,顯然不符合Hash演算法均勻分佈的原則。
反觀長度16或者其他2的冪,Length-1的值是所有二進位制位全為1,這種情況下,index的結果等同於HashCode後幾位的值。只要輸入的HashCode本身分佈均勻,Hash演算法的結果就是均勻的。
6:hashmap是執行緒安全的嗎?
不是。
6.2 為什麼?
因為沒加鎖
6.3 那在併發時會導致什麼問題?
hashmap在接近臨界點時,若此時兩個或者多個執行緒進行put操作,都會進行resize(擴容)和ReHash(為key重新計算所在位置),而ReHash在併發的情況下可能會形成連結串列環。
6.4 如何判斷有環形表?
最優:首先建立兩個指標A和B(在java裡就是兩個物件引用),同時指向這個連結串列的頭節點。然後開始一個大迴圈,在迴圈體中,讓指標A每次向下移動一個節點,讓指標B每次向下移動兩個節點,然後比較兩個指標指向的節點是否相同。如果相同,則判斷出連結串列有環,如果不同,則繼續下一次迴圈。
理解:此方法也可以用一個更生動的例子來形容:在一個環形跑道上,兩個運動員在同一地點起跑,一個運動員速度快,一個運動員速度慢。當兩人跑了一段時間,速度快的運動員必然會從速度慢的運動員身後再次追上並超過,原因很簡單,因為跑道是環形的。
7: hashmap 和 hashtable 區別?
執行緒: 不安全 安全(synchronized修飾)
效率: 更高 略低
陣列預設值: 16 11
null值: key-value都允許 不允許(拋異常)
其中key為null的map物件就在索引為0的位置上
8:那hashmap不安全,hashtable效能又低,怎麼辦?
用concurrenthashmap,即保證安全,效能又可以保障
8.1 那concurrenthashmap究竟是什麼?
整個ConcurrentHashMap的結構如下:
理解:hashmap是有entry陣列組成,而concurrenthashmap則是Segment陣列。那Segment是什麼呢?Segment本身就相當於一個HashMap物件。同HashMap一樣,Segment包含一個HashEntry陣列,陣列中的每一個HashEntry既是一個鍵值對,也是一個連結串列的頭節點。
單一的Segment結構如下(是不是看著就是hashmap):
像這樣的Segment物件,在ConcurrentHashMap集合中有多少個呢?有2的N次方個,共同儲存在一個名為segments的陣列當中。
可以說,ConcurrentHashMap是一個二級雜湊表。在一個總的雜湊表下面,有若干個子雜湊表。(這樣類比理解hashmap)
8.2:那他的put和get方法呢(對比hashmap的put和get方法)?
Put方法:
1.為輸入的Key做Hash運算,得到hash值。
2.通過hash值,定位到對應的Segment物件
3.獲取可重入鎖
4.再次通過hash值,定位到Segment當中陣列的具體位置。
5.插入或覆蓋HashEntry物件。
6.釋放鎖。
Get方法:
1.為輸入的Key做Hash運算,得到hash值。
2.通過hash值,定位到對應的Segment物件
3.再次通過hash值,定位到Segment當中陣列的具體位置。
由此可見,和hashmap相比,ConcurrentHashMap在讀寫的時候都需要進行二次定位。先定位到Segment,再定位到Segment內的具體陣列下標。
9: hashmap 和 concurrenthashmap區別?
執行緒: 不安全 安全
10:為啥concurrenthashmap和hashtable都是執行緒安全,但是前者效能更高
因為前者是用的分段鎖,根據hash值鎖住對應連結串列,當hash值不同時,使其能實現並行插入,效率更高,而hashtable會鎖住整個map
當需要put元素的時候,並不是對整個hashmap進行加鎖,而是先通過hashcode來知道他要放在那一個分段中,然後對這個分段進行加鎖,所以當多執行緒put的時候,只要不是放在同一個分段中,就實現了真正的並行的插入。
但是,在統計size的時候,就是獲取hashmap全域性資訊的時候,就需要獲取所有的分段鎖才能統計。
分段鎖的設計目的是細化鎖的粒度,當操作不需要更新整個陣列的時候,就僅僅針對陣列中的一部分行加鎖操作。
11.java7的hashmap和java8的hashmap的區別(1.8做了哪些優化)?
為了加快查詢效率(因為get()需要遍歷整張連結串列),java8的hashmap引入了紅黑樹結構,當某一連結串列的元素>8時,該連結串列就會轉成紅黑樹結構,查詢效率更高
相關文章
- 一篇文章帶你吃透 Docker 原理Docker
- HashMap原始碼分析 —— 一篇文章搞定HashMap面試HashMap原始碼面試
- 【架構視角】一篇文章帶你徹底吃透Spring架構Spring
- 一篇文章帶你入門Zookeeper
- 一篇文章帶你搞定經典面試題之扔雞蛋問題面試題
- 一篇文章帶你認識 SpringSecuritySpringGse
- 一篇文章帶你快速入門createjsJS
- 一篇文章帶你初步瞭解—CSS特指度CSS
- 一篇文章帶你瞭解和使用Promise物件Promise物件
- 一篇文章帶你瞭解HTML5 MathMLHTML
- 一篇文章帶你讀懂Redis的哨兵模式Redis模式
- # 一篇文章帶你入門軟體測試
- MySQL十種鎖,一篇文章帶你全解析MySql
- 一篇文章帶你瞭解——Kotlin協程Kotlin
- 一篇文章帶你瞭解介面自動化
- 一篇文章帶你掌握效能測試工具——JmeterJMeter
- 一篇文章帶你弄懂Kerberos的設計思路ROS
- 一篇文章帶你玩轉正規表示式
- 一篇文章帶你瞭解HTML格式化元素HTML
- 一篇文章帶你瞭解CSS 分頁例項CSS
- 一篇文章帶你搞懂 etcd 3.5 的核心特性
- 一篇文章帶你入門SQL程式設計GIFUSQL程式設計
- 一篇文章帶你瞭解高可用架構分析架構
- Truffle 2.0升級3.0升級指南
- 一篇文章帶你掌握Flex佈局的所有用法Flex
- 一篇文章帶你瞭解設計模式——建立者模式設計模式
- Zurmo – – 升級指南
- 一文帶你吃透作業系統作業系統
- mysql57小版本升級操作指南MySql
- 帶你逐步實現簡易HashMap,助力理解Java HashMapHashMapJava
- 一篇文章帶你瞭解設計模式——結構型模式設計模式
- 一篇文章搞定前端面試前端面試
- 一篇文章讓你明白你多級快取的分層架構快取架構
- 帶你走進Java集合之HashMapJavaHashMap
- Vue 3.0 升級指南Vue
- HTTPS 升級指南HTTP
- 害怕面試被問HashMap?這一篇就搞定了!面試HashMap
- 只需要這一篇,可以讓你 “吃透” 負載均衡負載