hashMap深入瞭解
平時一直在用hashMap但是從未去了解底層原始碼。
昨天的面試恰好提到這個問題,除了能回答是通過hash表去解決hash衝突外,則一無所知。今天就對hashMap的底層原始碼一探究竟。
1.繼承和實現關係
Map map = new hashMap()
一直以為是hashMap實現的map介面,現在才發現是hashMap不僅僅是實現了Map介面,還是繼承了AbstractMap,而AbstractMap也實現了Map介面。
首先來觀察頂層介面Map
- Map 介面
public interface Map<K,V>{ // 內部有一個Entry介面 // 類似於連結串列的節點,所以對map的遍歷,一般是對Entry的變數 interface Entry<K,V>{ //Entry 的 set/ get V 方法 …… } // 其餘就是增刪改查方法,沒什麼可看 …… }
- hashMap的結構
- 下圖是hashMap的內部類結構,一般常用的是KeySet和EntrySet
- 驚奇的發現居然也有樹的節點TreeNode(因為認為這是hash結構,不會用樹結構)
- 對keySet進行觀察
通過keySet()方法可以獲得KeySet結構
- 對EntrySet進行觀察
-
方法都相同,也應該相同,因為只是map不同形式的節點表達方式。
主要都用來遍歷Map集合。
瞭解到HashMap的基本結構之後,那麼就要開始對構造方法進行,瞭解 - 構造方法
有四個構造方法。看註釋文件,神器的發現有兩個引數因子對其效能有影響(原來所不知道的)
- 初始容量(the initial capacity)
- 載入因子(loadFactory)
API文件說明:HashMap 的例項有兩個引數影響其效能:初始容量 和載入因子。容量 是雜湊表中桶的數量,初始容量只是雜湊表在建立時的容量。載入因子 是雜湊表在其容量自動增加之前可以達到多滿的一種尺度。當雜湊表中的條目數超出了載入因子與當前容量的乘積時,則要對該雜湊表進行 rehash 操作(即重建內部資料結構),從而雜湊表將具有大約兩倍的桶數。
通常,預設載入因子 (.75) 在時間和空間成本上尋求一種折衷。載入因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設定初始容量時應該考慮到對映中所需的條目數及其載入因子,以便最大限度地減少 rehash 操作次數。如果初始容量大於最大條目數除以載入因子,則不會發生 rehash 操作。
【簡單 說,載入因子高,開闢空間小,容易rehash,載入因子小,開闢空間多,但是不容易rehash,空間換時間】(開闢空間 = 初始容量 / 載入因子)
執行緒安全問題
首先明確 hashMap是執行緒不安全的,因為沒有做任何執行緒處理(結構上的修改是指新增或刪除一個或多個對映關係的任何操作;僅改變與例項已經包含的鍵關聯的值不是結構上的修改。)這一般通過對自然封裝該對映的物件進行同步操作來完成。如果不存在這樣的物件,則應該使用 Collections.synchronizedMap方法來“包裝”該對映。最好在建立時完成這一操作,以防止對對映進行意外的非同步訪問,如下所示:
Map m = Collections.synchronizedMap(new HashMap(…));