Java集合之Map
阿新 • • 發佈:2021-06-23
一、Map的實現類的結構
Map:雙列資料,儲存key-value對的資料 --- 類似於高中的函式:y=f(x) (JDK1.2才有)
- HashMap:(1.2才有)作為Map的主要實現類;執行緒不安全,效率高;儲存null的key和value
- LinkedHashMap:(1.4才有)保證在遍歷map元素時,可以按照新增的順序實現遍歷。
- 原因:在原有的HashMap底層結構基礎上,添加了一對指標,指向前一個和後一個元素。
- 對於頻繁的遍歷操作,此類執行效率高於HashMap。
- TreeMap:(1.2才有)保證按照新增的key-value對進行排序,實現排序遍歷。此時考慮key的自然排序或定製排序,底層使用紅黑樹
- Hashtable:(1.0時就有)作為古老的實現類;執行緒安全的,效率低;不能儲存null的key和value
- Properties:常用來處理配置檔案。key和value都是String型別
HashMap的底層:
- 陣列+連結串列(jdk7及之前)
- 陣列+連結串列+紅黑樹(jdk8)
面試題:
- HashMap的底層實現原理?
- HashMap 和 Hashtable的異同?
- CurrentHashMap 與 Hashtable的異同?(暫時不講)
二、Map結構的理解
- Map中的key:無序的、不可重複的,使用Set儲存所有的key ---> key所在的類要重寫equals()和hashCode() (以HashMap為例)
- Map中的value:無序的、可重複的,使用Collection儲存所有的value ---> value所在的類要重寫equals()
- 一個鍵值對:key-value構成了一個Entry物件。
- Map中的entry:無序的、不可重複的,使用Set儲存所有的entry
三、HashMap的底層實現原理
以jdk7為例說明
在HashMap例項化以後,底層建立了長度是16的一維陣列Entry[] table。
--- 可能已經執行過多次put ---
map.put(key1,value1);
首先,呼叫key1所在類的hashCode()計算key1雜湊值,此雜湊值經過某種演算法計算以後,得到在Entry陣列中的存放位置。
- 如果此位置上的資料為空,此時的key1-value1新增成功。 ----情況1
- 如果此位置上的資料不為空,(意味著此位置上存在一個或多個數據(以連結串列形式存在)),比較key1和已經存在的一個或多個數據的雜湊值:
- 如果key1的雜湊值與已經存在的資料的雜湊值都不相同,此時key1-value1新增成功。 ----情況2
- 如果key1的雜湊值與已經存在的某一個數據(key2-value2)的雜湊值相同,繼續比較:呼叫key1所在類的equals(key2)方法,比較:
- 如果equals()返回false:此時key1-value1新增成功。 ----情況3
- 如果equals()返回true:使用value1替換value2。
補充:關於情況2和情況3:此時key1-value1和原來的資料以連結串列的方式儲存。
在不斷的新增過程中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時則擴容,預設的擴容方式:擴容為原來容量的2倍,並將原有的資料複製過來。
jdk8相較於jdk7在底層實現方面的不同
- new HashMap():底層沒有建立一個長度為16的陣列
- jdk8底層的陣列是:Node[],而非Entry[]
- 首次呼叫put()方法時,底層建立長度為16的陣列
- jdk7底層結構只有:陣列+連結串列。jdk8中底層結構:陣列+連結串列+紅黑樹。
在jdk8中,當陣列的某一個索引位置上的元素以連結串列形式存在的資料個數 > 8 且當前陣列的長度 > 64時,
此時此索引位置上的所有資料改為使用紅黑樹儲存。
HashMap原始碼中的一些常量
DEFAULT_INITIAL_CAPACITY:HashMap的預設容量,16
DEFAULT_LOAD_FACTOR:HashMap的預設載入因子:0.75
threshold:擴容的臨界值 = 容量填充因子 :160.75 = 12
TREEIFY_THRESHOLD:Bucket中連結串列長度大於該預設值,轉化為紅黑樹:8
MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64
四、LinkedHashMap的底層實現原理(瞭解)
原始碼中:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//能過記錄新增的元素的先後順序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}