Java集合(十)實現Map接口的HashMap


Java集合(十)繼承Map接口的HashMap

一、HashMap簡介(基於JDK1.8)

HashMap是基於哈希表(散列表),實現Map接口的雙列集合,數據結構是“鏈表散列”,也就是數組+鏈表 ,key唯一的value可以重復,允許存儲null 鍵null 值,元素無序。JDK1.8對HashMap進行一個大的優化,底層數據結構有“數組+鏈表”的形式,變成“數組+鏈表+紅黑樹”的形式,當鏈表長度超過閾值時,將鏈表轉換為紅黑樹,這樣大大減少了查找時間。

HashMap 的實例有兩個參數影響其性能:“初始容量” 和 “負載因子”。容量 是哈希表中桶的數量,初始容量 只是哈希表在創建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操作(即重建內部數據結構),從而哈希表將具有大約兩倍的桶數。
通常,默認負載因子是 0.75F, 這是在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地減少 rehash 操作次數。如果初始容量大於最大條目數除以加載因子,則不會發生 rehash 操作。

(一)、HashMap與Map接口的關系


(二)、數據結構

JDK 1.8 的 HashMap 的數據結構如下圖所示,當鏈表節點較少時仍然是以鏈表存在,當鏈表節點較多時(大於8)會轉為紅黑樹。


二、HashMap的繼承結構

從HashMap繼承結構和HashMap與Map接口關系圖,可以看出:

    • HashMap繼承於AbstractMap類,實現了Map接口。Map是"key-value鍵值對"接口,AbstractMap實現了"鍵值對"的通用函數接口。
    • HashMap是通過"拉鏈法(鏈地址法)"實現的哈希表。
    • HashMap實現了Cloneable接口,即實現了clone()方法clone()方法的作用很簡單,就是克隆一個HashMap對象並返回。
    • HashMap實現Serializable接口,分別實現了串行讀取、寫入功能。
      • 串行寫入函數是writeObject(),它的作用是將HashMap的“總的容量,實際容量,所有的Entry”都寫入到輸出流中。
      • 而串行讀取函數是readObject(),它的作用是將HashMap的“總的容量,實際容量,所有的Entry”依次讀出。

三、HashMap的構造方法

四、HashMap重要成員屬性

(一)、table

是一個Node<K,V>[]數組類型,Node<K,V>實現了Map.Entry接口,是鏈表的節點。哈希表的"key-value鍵值對"都是存儲在Node<K,V>數組中的。 

(二)、size

是HashMap的大小,它是HashMap保存的鍵值對的數量。

(三)、threshold

是HashMap的閾值,用於判斷是否需要調整HashMap的容量。threshold的值="容量 * 負載因子",當HashMap中存儲數據的數量達到threshold時,就需要將HashMap的容量加倍。

(四)、loadFactor

負載因子。當HashMap達到閾值時,負載因子 * 容量將HashMap擴容。

(五)、modCount

記錄HashMap修改的次數,主要用來是用來實現fail-fast機制的。

五、HashMap的遍歷

(一)、通過entrySet方法遍歷HashMap的鍵值對

1、通過entrySet()方法獲取HashMap“鍵值對”集合Set;

2、通過Iterator迭代器遍歷獲取的HashMap的“鍵值對”集合Set。

1 Set<Map.Entry<K,V>> set = map.entrySet();
2 Iterator iterator = set.iterator();
3 while(iterator.hasNext()) {
4     Object obj = iterator.next();
5 }
View Code

 

(二)、通過keySet方法遍歷HashMap的鍵

1、通過keySet()方法獲取HashMap“鍵”的Set集合;

2、通過Iterator迭代器遍歷獲取的HashMap的“鍵”集合Set。

1 Set<Map.Entry<K, V>> set = map.keySet();
2 Iterator iterator = set.iterator();
3 while(iterator.hasNext()) {
4     Object obj = iterator.next();
5 }
View Code

 

(三)、通過value方法遍歷HashMap的值

1、通過value()方法獲取HashMap“值”的Set集合;

2、通過Iterator迭代器遍歷獲取的HashMap的“值”集合Set。

1 Collection coll = map.values();
2 Iterator iterator = coll.iterator();
3 while(iterator.hasNext()) {
4     Object obj = iterator.next();
5 }
View Code

 

六、HashMap常用API

七、總結

1、HashMap 的底層是個 Node 數組(Node<K,V>[] table),在數組的具體索引位置,如果存在多個節點,則可能是以鏈表或紅黑樹的形式存在。

2、HashMap 的默認初始容量(capacity)是 16,capacity 必須為 2 的冪次方;默認負載因子(load factor)是 0.75;實際能存放的節點個數(threshold,即觸發擴容的閾值)= capacity * load factor。

3、HashMap 有 threshold 屬性和 loadFactor 屬性,但是沒有 capacity 屬性。初始化時,如果傳了初始化容量值,該值是存在 threshold 變量,並且 Node 數組是在第一次 put 時才會進行初始化,初始化時會將此時的 threshold 值作為新表的 capacity 值,然后用 capacity 和 loadFactor 計算新表的真正 threshold 值。

4、當同一個索引位置的節點在增加后達到 9 個時,並且此時數組的長度大於等於 64,則會觸發鏈表節點(Node)轉紅黑樹節點(TreeNode),轉成紅黑樹節點后,其實鏈表的結構還存在,通過 next 屬性維持。鏈表節點轉紅黑樹節點的具體方法為源碼中的 treeifyBin 方法。而如果數組長度小於64,則不會觸發鏈表轉紅黑樹,而是會進行擴容。

5、當同一個索引位置的節點在移除后達到 6 個時,並且該索引位置的節點為紅黑樹節點,會觸發紅黑樹節點轉鏈表節點。紅黑樹節點轉鏈表節點的具體方法為源碼中的 untreeify 方法。

6、HashMap 是非線程安全的,在並發場景下使用 ConcurrentHashMap 來代替。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM