Java Map接口详解
Map接口
Map接口概述
- Map與Collection并列存在。用于保存具有映射關系的數據:key-value
- Map 中的 key 和 value 都可以是任何引用類型的數據
- Map 中的 key 用Set來存放,不允許重復,即同一個 Map 對象所對應的類,須重寫hashCode()和equals()方法
- 常用String類作為Map的“鍵”
- key 和 value 之間存在單向一對一關系,即通過指定的 key 總能找到唯一的、確定的 value
- Map接口的常用實現類:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是 Map 接口使用頻率最高的實現類
Map接口常用方法
添加、刪除、修改操作:
-
Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中
-
void putAll(Map m):將m中的所有key-value對存放到當前map中
-
Object remove(Object key):移除指定key的key-value對,并返回value
-
void clear():清空當前map中的所有數據
元素查詢的操作: -
Object get(Object key):獲取指定key對應的value
-
boolean containsKey(Object key):是否包含指定的key
-
boolean containsValue(Object value):是否包含指定的value
-
int size():返回map中key-value對的個數
-
boolean isEmpty():判斷當前map是否為空
-
boolean equals(Object obj):判斷當前map和參數對象obj是否相等
元視圖操作的方法: -
Set keySet():返回所有key構成的Set集合
-
Collection values():返回所有value構成的Collection集合
-
Set entrySet():返回所有key-value對構成的Set集合
Map實現類之一:HashMap
- HashMap是 Map 接口使用頻率最高的實現類。
- 允許使用null鍵和null值,與HashSet一樣,不保證映射的順序。
- 所有的key構成的集合是Set:無序的、不可重復的。所以,key所在的類要重寫:equals()和hashCode()
- 所有的value構成的集合是Collection:無序的、可以重復的。所以,value所在的類要重寫:equals()
- 一個key-value構成一個entry
- 所有的entry構成的集合是Set:無序的、不可重復的
- HashMap 判斷兩個 key 相等的標準是:兩個 key 通過 equals() 方法返回 true,hashCode 值也相等。
- HashMap 判斷兩個 value相等的標準是:兩個 value 通過 equals() 方法返回 true。
HashMap的存儲結構
JDK 7及以前版本:HashMap是數組+鏈表結構(即為鏈地址法)
JDK 8版本發布以后:HashMap是數組+鏈表+紅黑樹實現。
JDK1.8之前
- HashMap的內部存儲結構其實是數組和鏈表的結合。當實例化一個HashMap時,系統會創建一個長度為Capacity的Entry數組,這個長度在哈希表中被稱為容量(Capacity),在這個數組中可以存放元素的位置我們稱之為“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素。
- 每個bucket中存儲一個元素,即一個Entry對象,但每一個Entry對象可以帶一個引用變量,用于指向下一個元素,因此,在一個桶中,就有可能生成一個Entry鏈。而且新添加的元素作為鏈表的head。
添加元素的過程:
向HashMap中添加entry1(key,value),需要首先計算entry1中key的哈希值(根據key所在類的hashCode()計算得到),此哈希值經過處理以后,得到在底層Entry[]數組中要存儲的位置i。如果位置i上沒有元素,則entry1直接添加成功。如果位置i上已經存在entry2(或還有鏈表存在的entry3,entry4),則需要通過循環的方法,依次比較entry1中key和其他的entry。如果彼此hash值不同,則直接添加成功。如果hash值不同,繼續比較二者是否equals。如果返回值為true,則使用entry1的value去替換equals為true的entry的value。如果遍歷一遍以后,發現所有的equals返回都為false,則entry1仍可添加成功。entry1指向原有的entry元素。
HashMap的擴容
當HashMap中的元素越來越多的時候,hash沖突的幾率也就越來越高,因為數組的長度是固定的。所以為了提高查詢的效率,就要對HashMap的數組進行擴容,而在HashMap數組擴容之后,最消耗性能的點就出現了:原數組中的數據必須重新計算其在新數組中的位置,并放進去,這就是resize。
那么HashMap什么時候進行擴容呢?
當HashMap中的元素個數超過數組大小(數組總大小length,不是數組中個數
size)loadFactor 時 , 就 會 進 行 數 組 擴 容 , loadFactor 的默認 值(DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值。也就是說,默認情況下,數組大小(DEFAULT_INITIAL_CAPACITY)為16,那么當HashMap中元素個數超過160.75=12(這個值就是代碼中的threshold值,也叫做臨界值)的時候,就把數組的大小擴展為 2*16=32,即擴大一倍,然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,所以如果我們已經預知HashMap中元素的個數,那么預設元素的個數能夠有效的提高HashMap的性能。
JDK1.8
- HashMap的內部存儲結構其實是數組+鏈表+樹的結合。當實例化一個HashMap時,會初始化initialCapacity和loadFactor,在put第一對映射關系時,系統會創建一個長度為initialCapacity的Node數組,這個長度在哈希表中被稱為容量(Capacity),在這個數組中可以存放元素的位置我們稱之為“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素。
- 每個bucket中存儲一個元素,即一個Node對象,但每一個Node對象可以帶一個引用變量next,用于指向下一個元素,因此,在一個桶中,就有可能生成一個Node鏈。也可能是一個一個TreeNode對象,每一個TreeNode對象可以有兩個葉子結點left和right,因此,在一個桶中,就有可能生成一個TreeNode樹。而新添加的元素作為鏈表的last,或樹的葉子結點。
那么HashMap什么時候進行擴容和樹形化呢?
當HashMap中的元素個數超過數組大小(數組總大小length,不是數組中個數size)loadFactor 時 , 就會進行數組擴容 , loadFactor 的默認 (DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值。也就是說,默認情況下,數組大(DEFAULT_INITIAL_CAPACITY)為16,那么當HashMap中元素個數超過160.75=12(這個值就是代碼中的threshold值,也叫做臨界值)的時候,就把數組的大小擴展為 2*16=32,即擴大一倍,然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,所以如果我們已經預知HashMap中元素的個數,那么預設元素的個數能夠有效的提高HashMap的性能。
當HashMap中的其中一個鏈的對象個數如果達到了8個,此時如果capacity沒有達到64,那么HashMap會先擴容解決,如果已經達到了64,那么這個鏈會變成樹,結點類型由Node變成TreeNode類型。當然,如果當映射關系被移除后,下次resize方法時判斷樹的結點個數低于6個,也會把樹再轉為鏈表。
總結:JDK1.8相較于之前的變化:
1 .HashMap map = new HashMap();//默認情況下,先不創建長度為16的數組
2 當首次調用map.put()時,再創建長度為16的數組
3 數組為Node類型,在jdk7中稱為Entry類型
4 形成鏈表結構時,新添加的key-value對在鏈表的尾部(七上八下)
5 當數組指定索引位置的鏈表長度>8時,且map中的數組的長度> 64時,此索引位置上的所有key-value對使用紅黑樹進行存儲。
HashMap源碼中的重要常量
DEFAULT_INITIAL_CAPACITY : HashMap的默認容量,16
MAXIMUM_CAPACITY : HashMap的最大支持容量,2^30
DEFAULT_LOAD_FACTOR:HashMap的默認加載因子
TREEIFY_THRESHOLD:Bucket中鏈表長度大于該默認值,轉化為紅黑樹
UNTREEIFY_THRESHOLD:Bucket中紅黑樹存儲的Node小于該默認值,轉化為鏈表
MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量。(當桶中Node的
數量大到需要變紅黑樹時,若hash表容量小于MIN_TREEIFY_CAPACITY時,此時應執行
resize擴容操作這個MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4
倍。)
table:存儲元素的數組,總是2的n次冪
entrySet:存儲具體元素的集
size:HashMap中存儲的鍵值對的數量
modCount:HashMap擴容和結構改變的次數。
threshold:擴容的臨界值,=容量*填充因子
loadFactor:填充因子
Map實現類之二:LinkedHashMap
- LinkedHashMap 是 HashMap 的子類
- 在HashMap存儲結構的基礎上,使用了一對雙向鏈表來記錄添加元素的順序
- 與LinkedHashSet類似,LinkedHashMap 可以維護 Map 的迭代
順序:迭代順序與 Key-Value 對的插入順序一致
Map實現類之三:TreeMap
- TreeMap存儲 Key-Value 對時,需要根據 key-value 對進行排序。
TreeMap 可以保證所有的 Key-Value 對處于有序狀態。 - TreeSet底層使用紅黑樹結構存儲數據
- TreeMap 的 Key 的排序:
?自然排序:TreeMap 的所有的 Key 必須實現 Comparable 接口,而且所有的 Key 應該是同一個類的對象,否則將會拋出ClasssCastException
?定制排序:創建 TreeMap 時,傳入一個 Comparator 對象,該對象負責對TreeMap 中的所有 key 進行排序。此時不需要 Map 的 Key 實現Comparable 接口 - TreeMap判斷兩個key相等的標準:兩個key通過compareTo()方法或者compare()方法返回0。
Map實現類之四:Hashtable
- Hashtable是個古老的 Map 實現類,JDK1.0就提供了。不同于HashMap,Hashtable是線程安全的。
- Hashtable實現原理和HashMap相同,功能相同。底層都使用哈希表結構,查詢速度快,很多情況下可以互用。
- 與HashMap不同,Hashtable 不允許使用 null 作為 key 和 value
- 與HashMap一樣,Hashtable 也不能保證其中 Key-Value 對的順序
- Hashtable判斷兩個key相等、兩個value相等的標準,與HashMap一致。
Map實現類之五:Properties
- Properties 類是 Hashtable 的子類,該對象用于處理屬性文件
- 由于屬性文件里的 key、value 都是字符串類型,所以 Properties 里的 key 和 value 都是字符串類型
- 存取數據時,建議使用setProperty(String key,String value)方法和getProperty(String key)方法
總結
以上是生活随笔為你收集整理的Java Map接口详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: IDA Pro的patch插件 KeyP
- 下一篇: 关于获取多个屏幕分辨率以及进行一些设置