HashMap根據鍵的hashCode值存儲數據,大多數情況下可以直接定位到它的值,因而具有很快的訪問速度,但遍歷順序卻不是確定的,HashMap最多只允許一條記錄的key為null,允許多條記錄的value為null,HashMap非線程安全,即任一時刻可以有多個線程同時寫HashMap,可能會導致數據的不一致,如果需要滿足線程安全,可以用Collections的synchronizedMap方法使HashMap具有線程安全的能力,或者使用ConcurrentHashMap。大方向上,HashMap 是一個數組,然后數組中每個元素是一個單向鏈表,很多實體都是嵌套類Entry的實例,Entry包含四個屬性:key、value、hash值和用於單向鏈表的next。
圖為 Java 7中HashMap的結構
- capacity:當前數組容量,始終保持 2^n,可以擴容,擴容后數組大小為當前的 2 倍。
- loadFactor:負載因子,默認為 0.75。
- threshold:擴容的閾值,等於 capacity * loadFactor。
比如當前的容器容量是16,負載因子是0.75,16*0.75=12,也就是說,當容量達到了12(擴容閾yu值)的時候,容器就會擴為當前容量大小的 2倍。
負載因子是0.75的時候,空間利用率比較高,而且避免了相當多的Hash沖突,使得底層的鏈表或者是紅黑樹的高度比較低,提升了空間效率。
上面是Java 7 對HashMap的實現,在JAVA 8中,利用了紅黑樹,所以在JAVA 8 是由數組+鏈表+紅黑樹組成。從JAVA 7中得知,在查找元素時候,可以根據hash值快速定位到數組具體的下標,但是后面的操作需要順着鏈表一個一個的比較下去才能找到所需值,時間復雜度取決於鏈表的長度,為O(n),為了降低這一部分處理的開銷,在JAVA 8中,當鏈表中的元素超過8個之后,會將鏈表轉換為紅黑樹,在這些位置進行查找的時候可降低時間復雜度 為O(logn),
圖為 JAVA 8中HashMap的結構
HashTable是一個線程安全的遺留類,很多映射的功能跟HashMap類似,不同的是它繼承Dictionary類,並且是線程安全的,任一時間只有一個線程可寫HashTable,並發行不好,可以在不需要線程安全的時候替換為HashMap,需要線程安全的時候可以用ConcurrentHashMap替換。
TreeMap可排序,實現了SortedMap接口,可以把它保存的記錄根據鍵排序,它默認是按鍵的升序排序,可以指定排序的比較器,當用迭代器(Iterator)遍歷TreeMap時,得到的記錄是排序過的。
在使用 TreeMap 時,key 必須實現 Comparable 接口或者在構造 TreeMap 傳入自定義的 Comparator,否則會在運行時拋出java.lang.ClassCastException類型的異常。 參考:https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html,寫的很好這個。
LinkedHashMap 是 HashMap 的一個子類,保存了記錄的插入順序,在用 Iterator 遍歷 LinkedHashMap時,先得到的記錄肯定是先插入的,也可以在構造時帶參數,按照訪問次序排序。