聲明:以下內容純屬個人理解,有不正確之處請積極指正!
HashMap
底層是什么結構恐锦?
HashMap底層是
數組+鏈表+紅黑樹
滑燃,紅黑樹是在JDK1.8的時候引入的役听,引入紅黑樹的目的是為了優(yōu)化過長的鏈表,也就是說1.7之前的結構都是數組+鏈表
。
初始化參數有哪些典予?
默認初始容量 DEFAULT_INITIAL_CAPACITY = 16甜滨;
默認加載因子 DEFAULT_LOAD_FACTOR = 0.75;
閾值 threshold =0瘤袖;
樹化閾值 TREEIFY_THRESHOLD = 8衣摩;
取消樹化閾值 UNTREEIFY_THRESHOLD = 6;
擴容機制是怎樣的捂敌?
擴容主要取決于調用哪個構造函數0纭(下文容量就是指數組的長度)
- 使用
new HashMap()
來創(chuàng)建實例,即沒有指定數組的初始容量占婉,此時數組還是null
泡嘴,那么當第一次執(zhí)行put(K key, V value)
方法添加元素時會初始化數組的容量為默認初始容量16,同時閾值 threshold = 16 * 0.75 = 12逆济。當數組的第13個位置添加了元素(當前數組已經有13個位置是非空酌予,不是指數組的下標),此時數組的元素個數 size 還是12(這里還沒有+1)奖慌,然后判斷++size>threshold
(13>12)成立抛虫,觸發(fā)擴容,將數組的容量設置為原容量的2倍简僧,閾值設置為原閾值的2倍莱褒,即此時 數組.length = 32,threshold = 24涎劈。 - 使用
new HashMap(int initialCapacity)
來創(chuàng)建實例广凸,則設置加載因子 loadFactor = 0.75,設置閾值 threshold 為大于 initialCapacity 并且是2的冪次方的數蛛枚,比如 initialCapacity = 20谅海,則 threshold = 25 = 32(源碼中是通過移位和或運算實現的,具體實現在 tableSizeFor(int cap) 方法)蹦浦;那么當第一次執(zhí)行put(K key, V value)
方法添加元素時會初始化數組的容量為 threshold 的值扭吁,所以當顯示指定數組的容量時,底層并不會按照你指定的容量來初始化數組盲镶,而是將數組的容量初始化為大于指定值并且是2的冪次方的值侥袜。然后當數組的第 25 個位置添加了元素(當前數組已經有25個位置是非空,不是指數組的下標)溉贿,此時數組的元素個數 size 還是24(這里還沒有+1)枫吧,然后判斷++size>threshold
(25>24)成立,觸發(fā)擴容宇色,將數組的容量設置為原容量的2倍九杂,閾值設置為原閾值的2倍颁湖,即此時 數組.length = 64,threshold = 48例隆。
上面就是 HashMap 常用的兩種創(chuàng)建方式并且相應的擴容方式甥捺,對照源碼可能會更容易理解。
是否線程安全镀层?
非線程安全镰禾!
若要使用線程安全Map的可以考慮使用 Collections.synchronizedMap() 或 ConcurrentHashMap。
如何保證hash散列唱逢?或者說如何避免hash碰撞吴侦?
(1). 首先要找到元素存儲在數組中的位置
找到數組的位置是通過一個簡單的計算,即tab[i = (n - 1) & hash]
惶我,等價于根據hash函數計算出的 hash
值對數組的長度也就是 length
取余妈倔,但取余的計算效率沒有位運算高,所以(n - 1) & hash也算是神來之筆绸贡,一個小的優(yōu)化吧盯蝴!
(2). 其次就是上面提到的hash函數,通過對 key 進行兩次hash運算听怕,增加hash復雜度捧挺。
- 第一次:先計算
h = key.hashCode()
; - 第二次:計算
h ^ (h >>> 16)
,這里右移16位的意義何在尿瞭?因為 h 這里是int類型闽烙,int在java中占4個字節(jié),每個字節(jié)是8位声搁,也就是32位黑竞,前16位為高位,后16位為低位疏旨,計算余數時(緊跟第(1)步)很魂,由于 n 比較小,hash 只有低16位參與了計算檐涝,高位的計算可以認為是無效的遏匆。這樣導致了計算結果只與低位信息有關,高位數據沒發(fā)揮作用谁榜。為了處理這個缺陷幅聘,我們可以讓 hash 的高16位數據與低16位數據進行異或運算,即 hash ^ (hash >>> 16)窃植。通過這種方式帝蒿,讓高位數據與低位數據進行異或,以此加大低位信息的隨機性撕瞧,變相的讓高位數據參與到計算中陵叽。
使用場景有哪些狞尔?
需要存儲 鍵值對 key-value
類型數據時適用丛版。
使用時要注意什么巩掺?
重寫equals和hashCode方法!R称琛胖替!
當我們用HashMap存入自定義的類時,如果不重寫這個自定義類的equals和hashCode方法豫缨,得到的結果會和我們預期的不一樣独令。當然了,這里主要指的是HashMap的key部分好芭!
對于HashMap燃箭,一般面試能說出這些應該也差不多了,如果是高級面試舍败,對于紅黑樹以及HashMap的更詳細實現要是能跟面試官侃侃而談當然是最好不過了招狸!
常用Map類的不同?
Map中常用的有 HashMap邻薯、LinkedHashMap裙戏、TreeMap。
HashMap
底層是數組+鏈表+紅黑樹
結構厕诡;LinkedHashMap
它是繼承自HashMap的累榜,也就是說基本和HashMap類似,唯一的不同點就是它的底層是數組+雙向鏈表+紅黑樹
灵嫌,引入雙向鏈表的目的是保證你插入map的元素的順序和你遍歷出來的元素的順序是一致的壹罚,這一點HashMap并沒有保證!TreeMap
它的底層是紅黑樹
結構的寿羞;
遍歷得到的元素是升序
排列的猖凛。
上面小結的也只是我目前了解的一些,如果想要了解更多稠曼,可以參考下面的幾篇博文形病,寫的很棒,分析的也很透徹霞幅!
參考: