1. HashMap
標准鏈地址法實現(下圖)。數組方式存儲key/value
,線程非安全,允許null
作為key
和value
,key
不可以重復,value
允許重復,不保證元素迭代順序是按照插入時的順序,key
的hash
值是先計算key
的hashcode
值,然后再進行計算,每次容量擴容會重新計算所以key
的hash
值,會消耗資源,要求key
必須重寫equals
和hashcode
方法。默認初始容量16
,加載因子0.75
,擴容為舊容量乘2
,查找元素快,如果key
一樣則比較value
,如果value
不一樣,則按照鏈表結構存儲value
。如果需要同步,可以用 Collections
的synchronizedMap
方法(Map m = Collections.synchronizedMap(new HashMap(…));
)。使HashMap
具有同步的能力,或者使用ConcurrentHashMap
。
JDK 1.8之后,加入了static final int TREEIFY_THRESHOLD = 8;
,當同一桶內元素個數超過8個,就會將鏈表結構進行樹化。
/** * The bin count threshold for using a tree rather than list for a * bin. Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */ static final int TREEIFY_THRESHOLD = 8;
2. HashTable
線程安全,不允許有null
的鍵和值,線程安全的,它在所有涉及到多線程操作的都加上了synchronized
關鍵字來鎖住整個table
,這就意味着所有的線程都在競爭一把鎖,在多線程的環境下,它是安全的,但是無疑是效率低下的。
3. ConcurrentHashMap
HashTable
有很多的優化空間,鎖住整個table
這么粗暴的方法可以變相的柔和點,比如在多線程的環境下,對不同的數據集進行操作時其實根本就不需要去競爭一個鎖,因為他們不同hash
值,不會因為rehash
造成線程不安全,所以互不影響,這就是鎖分離技術,將鎖的粒度降低,利用多個鎖來控制多個小的table
,這就是ConcurrentHashMap
JDK1.7版本的核心思想。
JDK1.7的實現
在JDK1.7版本中,ConcurrentHashMap
的數據結構是由一個Segment
數組和多個HashEntry
組成,如下圖所示:
Segment
數組的意義就是將一個大的table
分割成多個小的table
來進行加鎖,也就是上面的提到的鎖分離技術,而每一個Segment
元素存儲的是HashEntry
數組+鏈表,這個和HashMap
的數據存儲結構一樣
JDK1.8的實現
JDK1.8的實現已經摒棄了Segment
的概念,而是直接用Node
數組+鏈表+紅黑樹的數據結構來實現,並發控制使用Synchronized
和CAS
來操作,整個看起來就像是優化過且線程安全的HashMap
,雖然在JDK1.8中還能看到Segment
的數據結構,但是已經簡化了屬性,只是為了兼容舊版本。
4. TreeMap
TreeMap
實現SortMap
接口,基於紅黑二叉樹的NavigableMap
的實現,線程非安全,不允許null
,key
不可以重復,value
允許重復,存入TreeMap
的元素應當實現Comparable
接口或者實現Comparator
接口,會按照排序后的順序迭代元素,兩個相比較的key
不得拋出classCastException
。主要用於存入元素的時候對元素進行自動排序,迭代輸出的時候就按排序順序輸出
5. LinkedHashMap
LinkedHashMap
是HashMap
的一個子類,保存了記錄的插入順序,在用Iterator
遍歷LinkedHashMap
時,先得到的記錄肯定是先插入的。也可以在構造時用帶參數,按照應用次數排序。在遍歷的時候會比HashMap
慢,不過有種情況例外,當HashMap
容量很大,實際數據較少時,遍歷起來可能會比LinkedHashMap
慢,因為LinkedHashMap
的遍歷速度只和實際數據有關,和容量無關,而HashMap
的遍歷速度和他的容量有關。
6. WeakHashMap
WeakHashMap
,從名字可以看出它是某種 Map
,允許null
作為key
和value
(Both null values and the null key are supported.),非線程安全(this class is not synchronized)。它的特殊之處在於 WeakHashMap
里的entry
可能會被GC
自動刪除,即使程序員沒有調用remove()
或者clear()
方法。
更直觀的說,當使用 WeakHashMap
時,即使沒有顯示的添加或刪除任何元素,也可能發生如下情況:
- 調用兩次
size()
方法返回不同的值;- 兩次調用
isEmpty()
方法,第一次返回false
,第二次返回true
;- 兩次調用
containsKey()
方法,第一次返回true
,第二次返回false
,盡管兩次使用的是同一個key
;- 兩次調用
get()
方法,第一次返回一個value
,第二次返回null
,盡管兩次使用的是同一個對象。
WeekHashMap
的這個特點特別適用於需要緩存的場景。在緩存場景下,由於內存是有限的,不能緩存所有對象;對象緩存命中可以提高系統效率,但緩存MISS也不會造成錯誤,因為可以通過計算重新得到。
要明白WeekHashMap
的工作原理,還需要引入一個概念:弱引用(WeakReference)。Java中內存是通過GC自動管理的,GC會在程序運行過程中自動判斷哪些對象是可以被回收的,並在合適的時機進行內存釋放。GC判斷某個對象是否可被回收的依據是,是否有有效的引用指向該對象。如果沒有有效引用指向該對象(基本意味着不存在訪問該對象的方式),那么該對象就是可回收的。這里的“有效引用”並不包括弱引用。也就是說,雖然弱引用可以用來訪問對象,但進行垃圾回收時弱引用並不會被考慮在內,僅有弱引用指向的對象仍然會被GC回收。
WeakHashMap
內部是通過弱引用來管理entry
的,弱引用的特性對應到WeakHashMap
上意味:將一對key, value
放入到WeakHashMap
里並不能避免該key
值被GC
回收,除非在WeakHashMap
之外還有對該key
的強引用。