文章目錄
- HashMap 和 Hashtable 實作類
- LinkedHashMap 實作類
- Properties 類
- SortedMap 接口和 TreeMap 實作類
- IdentityHashMap 實作類
- EnumMap 實作類
- 各 Map 實作類的性能分析
圖一:Map集合結構圖

Map用于儲存具有映射關系的資料,是以 Map 集合裡儲存着兩組值 , 一組值用于儲存 Map 裡的 key 另外一組值用于儲存 Map 裡的 value , key 和 value 都
可以是任何引用類型的資料 。 Map 的 key 不允許重複 ,即同一個 Map 對象的任何兩個 key 通過 equals 方法比較總是傳回 false 。
key 和 value 之間存在單向一對一關系, 即通過指定的 key,總能找到唯一的、确定的 value 。 從 Map 中取出數資料時隻要給出指定的 key , 就可以取出對應的value 。 如果把 Map 的兩組值拆開來看 , Map 裡 的資料有如圖二所示的結構 。
圖二:分開看 Map 的 key 組和 value 組
從圖二中可以看出 ,如果把 Map 裡 的所有 key資料組放在一起來看,它們就組成了一個 Set 集合(所有的 key 沒有順序, key 與 key 之間不能重複) ,實際Map 确實包含了 一個 keySet()方法 ,用于傳回 Map 裡所有 key 組成的 Set 集合 。
實際上,Map的實作類和子接口中 key 集的存儲形式和對應Set 集合中元素的存儲形式完全相同 。
Set 與 Map 之間的關系非常密切 。 雖然 Map中放的元素是 key-value 對 , Set 集合中放的元素是單個對象 , 但如果把 key-value 對中的 value 當成 key 的 附庸 : key 在哪裡, value就跟在哪裡 。 這樣就可以像對待 Set 一樣來對待 Map 了 。 事實上, Map 提供了 一個 Entry内部類來封裝 key-value對 , 而計算 Entry存儲時則隻考慮 Enume封裝的 key。從 Java源代碼來看, Java則是先實作了 Map ,然後通過包裝一個所有 Value都為null的Map來實作Set集合 。
如果把 Map 裡的所有 value 放在一起來看,它們又非常類似于一個 List: 元素與元素之間可以重複,每個元素可以根據索引來查找 , 隻是 Map 中的索引不再使用整數值,而是以另 一 個對象作為索引 。 如果需要從 List 集合中取出元素,則需要提供該元素的數字索引 ; 如果需要從 Map 中取出元素, 則需要提供該元素的 key 索引 。 是以 , Map 有時也被稱為字典,或關聯數組 。
Map 接口中定義了如下常用的方法 :
- void clear(): 删除該 Map 對象中的所有 key-value 對 。
- boolean containsKey(Object key): 查詢 Map 中 是否包含指定的 key ,如果包含則傳回 true 。
- boolean containsValue(Object value): 查詢 Map 中 是否包含一個或多個 value ,如果包含則傳回 true ,
- Set entrySet(): 傳回 Map 中包含 的 key-value 對所組成的 Set 集合,每個集合元素都是 Map.Entry(Entry 是 Map 的内部類)對象 。
- Object get(Object key): 傳回指定 key 所對應的 value; 如果此 Map 中不包含該 key ,則 傳回 null 。
- boolean isEmpty(): 查詢該 Map 是否為空(即不包含任何 key-value 對 ) ,如 果為空則傳回 true 。
-
Set keySet(): 傳回該 Map 中所有 key 組成的 Set 集合 。
Object put(Object key, Object value): 添加 一個 key-value 對,如果目前 Map 中己有一個與該 key相等的 key-value 對,則新的 key-value 對會覆寫原來的 key-value 對 。
- void putAll(Map m): 将指定 Map 中的 key-value 對複制到本 Map 中 。
- Object remove(Object key): 删除指定 key 所對應的 key-value 對,傳回被删除 key 所關聯的 value ,如果該 key 不存在,則傳回 null 。
- boolean remove(Object key, Object value): 這是 Java 8 新增的方法,删除指定 key 、 value 所對應的 key-value 對 。 如果從該 Map 中成功地删除該 key-value 對,該方法傳回 true ,否則傳回 false 。
- int sizeO : 傳回該 Map 裡的 key-value 對的個數 。
- Collection values() : 傳回該 Map 裡所有 value 組成的 Collection 。
Map 中包括一個内部類 Entry, 該類封裝了 一個 key-value 對 。 Entry 包含如下三個方法:
- Object getKey(): 傳回該 En町裡包含的 key 值 。
- Object getValue(): 傳回該 Entry 裡包含的 value 值 。
- Object setValue(V value): 設定該 Entry 裡包含的 value 值,井傳回新設定的 value 值 。
下面程式示範了 Map 的基本功能:
MapTest.java
public class MapTest
{
public static void main(String[] args)
{
Map map = new HashMap();
// 成對放入多個key-value對
map.put("瘋狂Java講義" , 109);
map.put("瘋狂iOS講義" , 10);
map.put("瘋狂Ajax講義" , 79);
// 多次放入的key-value對中value可以重複
map.put("輕量級Java EE企業應用實戰" , 99);
// 放入重複的key時,新的value會覆寫原有的value
// 如果新的value覆寫了原有的value,該方法傳回被覆寫的value
System.out.println(map.put("瘋狂iOS講義" , 99)); // 輸出10
System.out.println(map); // 輸出的Map集合包含4個key-value對
// 判斷是否包含指定key
System.out.println("是否包含值為 瘋狂iOS講義 key:"
+ map.containsKey("瘋狂iOS講義")); // 輸出true
// 判斷是否包含指定value
System.out.println("是否包含值為 99 value:"
+ map.containsValue(99)); // 輸出true
// 擷取Map集合的所有key組成的集合,通過周遊key來實作周遊所有key-value對
for (Object key : map.keySet() )
{
// map.get(key)方法擷取指定key對應的value
System.out.println(key + "-->" + map.get(key));
}
map.remove("瘋狂Ajax講義"); // 根據key來删除key-value對。
System.out.println(map); // 輸出結果不再包含 瘋狂Ajax講義=79 的key-value對
}
}
Java 8 除為 Map 增加 了 remove(Object key , Object value)預設方法之外,還增加了如下方法:
- Object compute(Object key, BiFunction remappingFunction) : 該方法使用 remappingFunction 根據原 key-value 對計算一個新 value 。 隻要新 value 不為 null. 就使用新 value 覆寫原 value ; 如果原value 不為 null ,但新 value 為 null ,則删除原 key-value 對;如果原 value 、新 value 同時為 null ,那麼該方法不改變任何 key-value 對,直接傳回 null 。
- Object computeIfAbsent(Object key, Function mappingFunction): 如果傳給該方法的 key 參數在Map 中對應的 value 為 null ,則使用 mappingFunction 根據 key 計算一個新的結果,如果計算結果不為 null ,則用計算結果覆寫原有的value 。 如果原 Map 原來不包括該 key,那麼該方法可能會添加一組 key-value 對 。
- Object computeIfPresent(Object key, BiFunction remappingFunction): 如果傳給該方法的 key 參數在 Map 中對應的 value 不為 null. 該方法将使用 remappingFunction 根據原 key、 value 計算一個新的結果,如果計算結果不為 null. 則使用該結果覆寫原來的 value; 如果計算結果為 null.則删除原 key-value 對 。
- void forEach(BiConsumer action): 該方法是 Java 8 為 Map 新增的 一個周遊 key-value 對的方法,通過該方法可以更簡潔地周遊 Map 的 key-value 對 。
- Object getOrDefault(Object key, V defaultValue) : 擷取指定 key 對應的 value 。 如果該 key 不存在 ,則傳回 defaultValue 。
- Object merge(Object key, Object value, BiFunction remappingFunction): 該方法會先根據 key 參數擷取該 Map 中對應的 value 。 如果擷取的 value 為 null. 則直接用傳入的 value 覆寫原有的 value(在這種情況下,可能要添加一組 key-value 對) ;如果擷取的 value 不為 null ,則使用remappingFunction 函數根據原 value 、 新 value 計算 一個新的結果,并用得到的結果去覆寫原有的 value 。
- Object putlfAbsent(Object key, Object value): 該方法會自動檢測指定 key 對應的 value 是否為 null,如果該 key 對應的 value 為 null. 該方法将會用新 value 代替原來的 null 值 。
- Object replace(Object key, Object value): 将 Map 中指定 key 對應的 value 替換成新 value 。與傳統put()方法不同的是 , 該方法不可能添加新的 key-value 對 。 如果嘗試替換 的 key 在原 Map 中不存在,該方法不會添加 key-value 對,而是傳回 null 。
- boolean replace(K key, V oldValue, V newValue): 将 Map 中指定 key-value 對的原 value 替換成新value 。 如果在 Map 中找到指定的 key-value 對,則執行替換并傳回 true ,否則傳回 false 。
- replaceAll(BíFunction function): 該力活使用 BiFunction 别原 key-value 對執行計算,并将計算結果作為該 key-value 對的 value 值 。Object computeIfAbsent(Object key, Function mappingFunction): 如果傳給該方法的 key 參數在Map 中對應的 value 為 null ,則使用 mappingFunction 根據 key 計算一個新的結果,如果計算結果不為 null ,則用計算結果覆寫原有的 value 。 如果原 Map 原來不包括該 key,那麼該方法可能會添加一組 key-value 對 。
- Object computeIfPresent(Object key, BiFunction remappingFunction): 如果傳給該方法的 key 參數在 Map 中對應的 value 不為 null. 該方法将使用 remappingFunction 根據原 key、 value 計算一個新的結果,如果計算結果不為 null. 則使用該結果覆寫原來的 value; 如果計算結果為 null.則删除原 key-value 對 。
- Object merge(Object key, Object value, BiFunction remappingFunction): 該方法會先根據 key 參數擷取該 Map 中對應的 value 。 如果擷取的 value 為 null. 則直接用傳入的 value 覆寫原有的 value(在這種情況下,可能要添加一組 key-value 對) ;如果擷取的 value 不為 null ,則使用remappingFunction 函數根據原 value 、 新 value 計算 一個新的結果,并用得到的結果去覆寫原有的value 。
- Object putlfAbsent(Object key, Object value): 該方法會自動檢測指定 key 對應的 value 是否為 null,如果該 key 對應的 value 為 null. 該方法将會用新 value 代替原來的 null 值 。
- Object replace(Object key, Object value): 将 Map 中指定 key 對應的 value 替換成新 value 。與傳統put()方法不同的是 , 該方法不可能添加新的 key-value 對 。 如果嘗試替換 的 key 在原 Map 中不存在,該方法不會添加 key-value 對,而是傳回 null 。
- boolean replace(K key, V oldValue, V newValue): 将 Map 中指定 key-value 對的原 value 替換成新value 。 如果在 Map 中找到指定的 key-value 對,則執行替換并傳回 true ,否則傳回 false 。
- replaceAll(BíFunction function): 該力活使用 BiFunction 别原 key-value 對執行計算,并将計算結果作為該 key-value 對的 value 值 。
API: java.util.Map
圖三:HashMap繼承示意圖
HashMap 和 Hashtable 都是 Map 接口的典型實作類,它們之間的關系完全類似于 ArrayList 和 Vector的關系。
Java 8 改進了 HashMap 的實作,使用 HashMap 存在 key 沖突時依然具有較好的性能。
除此之外 , Hashtable 和 HashMap 存在兩點典型差別 。
- Hashtable 是一個線程安全的 Map 實作,但 HashMap 是線程不安全的實作,是以 HashMap 比Hashtable 的性能高一 點;但如果有多個線程通路同 一個 Map 對象時,使用 Hashtable 實作類會更好。
- Hashtable 不允許使用 null 作為 key 和 value ,如果試圖把 null 值放進 Hashtable 中,将會引發 NullPointerException 異常 ; 但 HashMap 可以使用 null 作為 key 或 value 。
與 Vector 類似的是,應當盡量少用 Hashtable實作類,即使需要建立線程安全的 Map 實作類,也無須使用 Hashtable 實作類,可以通過 Collections 工具類把 HashMap 變成線程安全的 。
為了成功地在 HashMap 、 Hashtable 中存儲、擷取對象,用作 key 的對象必須實作 hashCode()方法和 equals()方法 。
與 HashSet 集合不能保證元素的順序一樣, HashMap 、 Hashtable 也不能保證其中 key-value 對的順序 。 類似于 HashSet , HashMap 、 Hashtable 判斷兩個 key 相等的标準也是:兩個 key 通過 equals()方法比較傳回 true ,兩個 key 的 hashCode 值也相等 。
除此之外, HashMap 、 Hashtable 中還包含一個 containsValue()方法,用于判斷是否包含指定的 value 。
HashMap 、 Hashtable 判斷兩個 value 相等的标準更簡單 : 隻要兩個對象通過 equals()方法比較傳回 true 即可 。
下面程式示範了 Hashtable 判斷兩個 key相等的标準和兩個 value 相等的标準 :
HashtableTest.java
class A
{
int count;
public A(int count)
{
this.count = count;
}
// 根據count的值來判斷兩個對象是否相等。
public boolean equals(Object obj)
{
if (obj == this)
return true;
if (obj != null && obj.getClass() == A.class)
{
A a = (A)obj;
return this.count == a.count;
}
return false;
}
// 根據count來計算hashCode值。
public int hashCode()
{
return this.count;
}
}
class B
{
// 重寫equals()方法,B對象與任何對象通過equals()方法比較都傳回true
public boolean equals(Object obj)
{
return true;
}
}
public class HashtableTest
{
public static void main(String[] args)
{
Hashtable ht = new Hashtable();
ht.put(new A(60000) , "瘋狂Java講義");
ht.put(new A(87563) , "輕量級Java EE企業應用實戰");
ht.put(new A(1232) , new B());
System.out.println(ht);
// 隻要兩個對象通過equals比較傳回true,
// Hashtable就認為它們是相等的value。
// 由于Hashtable中有一個B對象,
// 它與任何對象通過equals比較都相等,是以下面輸出true。
System.out.println(ht.containsValue("測試字元串")); // ① 輸出true
// 隻要兩個A對象的count相等,它們通過equals比較傳回true,且hashCode相等
// Hashtable即認為它們是相同的key,是以下面輸出true。
System.out.println(ht.containsKey(new A(87563))); // ② 輸出true
// 下面語句可以删除最後一個key-value對
ht.remove(new A(1232)); //③
System.out.println(ht);
}
}
值得一提的是: 雖然容器号稱存儲的是 Java 對象,但實際上并不會真正将 Java 對象放入容器中,隻是在容器中保留這些對象的引用,而這些引用變量指向了我們要實際儲存的 Java 對象。
圖四:HashMap存儲對象示意圖
java.util.HashMap java.util.Hashtable
HashSet 有一個 LinkedHashSet 子類, HashMap 也有 一個 LinkedHashMap 子類 ; LinkedHashMap 也使用雙向連結清單來維護 key-value 對的次序(其實隻需要考慮、 key 的次序) , 該連結清單負責維護 Map 的疊代順序,疊代順序與 key-value 對的插入順序保持一緻 。
LinkedHashMap 可以避免對 HashMap 、 Hashtable 裡的 key-value 對進行排序(隻要插入 key-value對時保持順序即可),同時又可避免使用 TreeMap 所增加的成本 。
LinkedHashMap 需要維護元素的插入順序,是以性能略低于 HashMap 的性能;但因為 它 以連結清單來維護内部順序,是以在疊代通路 Map 裡的全部元素時将有較好的性能 。 下面程式示範了 LinkedHashMap的功能:疊代輸出 LinkedHashMap 的元素時,将會按添加 key-value 對的順序輸出 :
LinkedHashMapTest.java
public class LinkedHashMapTest
{
public static void main(String[] args)
{
LinkedHashMap scores = new LinkedHashMap();
scores.put("國文" , 80);
scores.put("英文" , 82);
scores.put("數學" , 76);
// 調用forEach方法周遊scores裡的所有key-value對
scores.forEach((key, value) -> System.out.println(key + "-->" + value));
}
}
java.util.LinkedHashMap
Properties 類是 Hashtable 類的子類 ,該類對象在處理屬性檔案時特别友善 (Windows 操作平台上的ini檔案就是一種屬性檔案)。 Properties 類可 以把 Map對象和屬性檔案關聯起來,進而可以把 Map 對象中的 key-value 對寫入屬性檔案中,也可以把屬性檔案中的"屬性名=屬性值 "加載到 Map 對象中 。 由于屬性檔案裡的屬性名、屬性值隻能是字元串類型,是以 Properties 裡 的 key、value 都是字元串類型。
該類提供了如下三個方法來修改 Properties 裡的 key 、 value 值 :
- String getProperty(String key): 擷取 Properties 中指定屬性 名對應 的屬性值,類似于 Map 的get(Object key)方法 。
- String getProperty(String key, String defaultValue): 該方法與前一個方法基本相似 。 該方法多一個功能,如果 Properties 中不存在指定的 key 時,則該方法指定預設值 。
- Object setProperty(String key, String value): 設定屬 性值,類似于 Hashtable 的 put()方法 。除此之外,它還提供了兩個讀寫屬性檔案的方法 。
- void load(InputStream inStream): 從屬性檔案 ( 以輸入流表示)中加載 key-value 對,把加載到的key-value 對追加到 Properties 裡( Properties 是 Hash table 的 子類,它不保證 key-value 對之間的次序)。
- void store(OutputStream out, String comments): 将 Properties 中的 key-value 對輸出到指定的屬性檔案(以輸出流表示)中。
下面程式示範了 Properties 類的用法:
PropertiesTest.java
public class PropertiesTest
{
public static void main(String[] args)
throws Exception
{
Properties props = new Properties();
// 向Properties中增加屬性
props.setProperty("username" , "yeeku");
props.setProperty("password" , "123456");
// 将Properties中的key-value對儲存到a.ini檔案中
props.store(new FileOutputStream("a.ini")
, "comment line"); //①
// 建立一個Properties對象
Properties props2 = new Properties();
// 向Properties中增加屬性
props2.setProperty("gender" , "male");
// 将a.ini檔案中的key-value對追加到props2中
props2.load(new FileInputStream("a.ini") ); //②
System.out.println(props2);
}
}
java.util.Properties
圖五:TreeMap繼承示意圖
正如 Set 接口派生出 SortedSet 子接口, SortedSet 接口有一個 TreeSet 實作類一樣, Map 接口也派生出一個 SortedMap 子接口, SortedMap 接口也有一個 TreeMap 實作類 。
TreeMap 就是 一個紅黑樹資料結構 , 每個 key-value 對即作為紅黑樹的一個節點 。 TreeMap 存儲key-value 對(節點)時, 需要根據 key 對節點進行排序 。 TreeMap 可以保證所有的 key-value 對處于有序狀态。
圖六:TreeMap存儲資料結構示意圖
TreeMap 也有兩種排序方式:
- 自然排序 : TreeMap 的所有 key 必須實作 Comparable 接口,而且所有的 key 應該是同一個類的對象,否則将會抛出 ClassCastException 異常 。
- 定制排序 : 建立 TreeMap 時,傳入一個 Comparator 對象, 該對象負責對 TreeMap 中的所有 key進行排序 。 采用定制排序時不要求 Map的 key 實作 Comparable 接口 。
類似于 TreeSet 中判斷兩個元素相等的标準. TreeMap 中判斷兩個 key 相等的标準是:兩個 key 通過 compareTo()方法傳回 0, TreeMap 即認為這兩個 key 是相等的 。
與 TreeSet 類似的是. TreeMap 中也提供了 一系列根據 key 順序通路 key-value 對的方法。
- Map.Entry fIrstEntry(): 傳回該 Map 中 最小 key 所對應的 key-value 對,如果該 Map 為 空, 則傳回 null 。
- Object fIrstKey(): 傳回該 Map 中的最小 key 值,如果該 Map 為空,則傳回 null 。
- Map.Entry lastEntryO: 傳回該 Map 中 最大 key 所對應的 key-value 對,如果該 Map 為空或不存在這樣的 key-value對,則都傳回 null 。
- Object lastKeyO: 傳回該 Map 中的最大 key 值,如果該 Map 為空或不存在這樣的 key. 則都傳回 null 。
- Map.Entry higherEntry(Object key): 傳回 該 Map 中位于 key 後 一位 的 key-value 對 ( 即大于指定key 的最小 key 所對應的 key-value 對) 。 如果該 Map 為 空 ,則傳回 null 。
- Object higherKey(Object key): 傳回 該 Map 中 位于 key 後一位的 key 值(即大于指定 key 的 最小key 值) 。 如果 該 Map 為 空或不存在這樣 的 key-value 對,則都傳回 null 。
- Map.En町, lowerEntry(Object key): 傳回該 Map 中 位于 key 前一位的 key-value 對 ( 即小于指定key 的最大 key 所對應的 key-value 對 ) 。 如果該 Map 為 空或不存在這樣 的 key-value 對,則都傳回 null 。
-
Object lowerKey(Object key): 傳回該 Map 中位于 key 前一位的 key 值(即小于指定 key 的最大
key 值) 。 如果 該 Map 為 空或不存在這樣的 key. 則都返 回 null 。
- NavigableMap subMap(Object 台omKey, boolean fromInclusive, Object toKey, boolean toInclusive):傳回該 Map 的子 Map. 其 key 的範圍是從 fromKey (是否包括取決于第二個參數) 到 toKey (是否包括取決于第四個參數) 。
- SortedMap subMap(Object fromKey, Object toKey): 傳回該 Map 的子 Map. 其 key 的 範圍是從fromKey (包括)到 toKey (不包括) 。
- SortedMap tailMap(Object fromKey): 傳回該 Map 的子 Map. 其 key 的 範圍是大于fomKey (包括)的所有 key 。
- NavigableMap tailMap(Object fromKey, boolean inclusive): 傳回該 Map 的子 Map. 其 key 的範圍是大于 fromKey (是否包括取決于第二個參數)的所有 key 。
- SortedMap headMap(Object toKey): 返 回該 Map 的子 Map 。其 key 的範圍是小于 toKey (不包括)的所有 key 。
- NavigableMap headMap(Object toKey,boolean inclusive): 傳回 該 Map 的子 Map。其 key 的範圍是小于 toKey (是否包括取決于第二個參數) 的所有 key 。
下面以自然排序為例,介紹 TreeMap 的基本用法:
TreeMapTest.java
class R implements Comparable
{
int count;
public R(int count)
{
this.count = count;
}
public String toString()
{
return "R[count:" + count + "]";
}
// 根據count來判斷兩個對象是否相等。
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj != null && obj.getClass() == R.class)
{
R r = (R)obj;
return r.count == this.count;
}
return false;
}
// 根據count屬性值來判斷兩個對象的大小。
public int compareTo(Object obj)
{
R r = (R)obj;
return count > r.count ? 1 :
count < r.count ? -1 : 0;
}
}
public class TreeMapTest
{
public static void main(String[] args)
{
TreeMap tm = new TreeMap();
tm.put(new R(3) , "輕量級Java EE企業應用實戰");
tm.put(new R(-5) , "瘋狂Java講義");
tm.put(new R(9) , "瘋狂Android講義");
System.out.println(tm);
// 傳回該TreeMap的第一個Entry對象
System.out.println(tm.firstEntry());
// 傳回該TreeMap的最後一個key值
System.out.println(tm.lastKey());
// 傳回該TreeMap的比new R(2)大的最小key值。
System.out.println(tm.higherKey(new R(2)));
// 傳回該TreeMap的比new R(2)小的最大的key-value對。
System.out.println(tm.lowerEntry(new R(2)));
// 傳回該TreeMap的子TreeMap
System.out.println(tm.subMap(new R(-1) , new R(4)));
}
}
java.util.TreeMap java.util.SortedMap
這個 Map 實作類的實作機制與 HashMap 基本相似,但它在處理兩個 key 相等時比較獨特:在IdentityHashMap 中,當且僅當兩個 key 嚴格相等 (key1 == key2) 時, IdentityHashMap 才認為兩個 key相等:對于普通的 HashMap 而言,隻要 key1和 key2 通過 equals()方法比較傳回 true ,且它們的 hashCode值相等即可。
在實作對象周遊算法(如對象串行化)時, 這個類非常有用, 可以用來跟蹤每個對象的周遊狀況。
IdentityHashMapTest.java
public class IdentityHashMapTest
{
public static void main(String[] args)
{
IdentityHashMap ihm = new IdentityHashMap();
// 下面兩行代碼将會向IdentityHashMap對象中添加兩個key-value對
ihm.put(new String("國文") , 89);
ihm.put(new String("國文") , 78);
// 下面兩行代碼隻會向IdentityHashMap對象中添加一個key-value對
ihm.put("java" , 93);
ihm.put("java" , 98);
System.out.println(ihm);
}
}
java.util.IdentityHashMap
EnumMap 是一個與枚舉類一起使用的 M叩實作, EnumMap 中的所有 key 都必須是單個枚舉類的枚舉值 。 建立 EnumMap 時必須顯式或隐式指定它對應的枚舉類。 EnumMap 具有如下特征。
- Enur出1ap 在内部以數組形式儲存,是以這種實作形式非常緊湊、高效。
- EnumMap 根據 key 的自然順序(即枚舉值在枚舉類中的定義順序)來維護 key-value 對的順序。當程式通過 keySet() 、 entrySet() 、 values()等方法周遊 EnumMap 時可以看到這種順序 。
- EnumMap 不允許使用 null 作為 key , 但允許使用 null 作為 value 。如果試圖使用 null 作為 key時将抛出 NullPointerException 異常。如果隻是查詢是否包含值為 null 的 key,或隻是删除值為null 的 key ,都不會抛出異常。
與建立普通的 Map 有所差別的是,建立 EnumMap 時必須指定一個枚舉類,進而将該 EnurnMap 和指定枚舉類關聯起來 :
EnumMapTest.java
enum Season
{
SPRING,SUMMER,FALL,WINTER
}
public class EnumMapTest
{
public static void main(String[] args)
{
// 建立EnumMap對象,該EnumMap的所有key都是Season枚舉類的枚舉值
EnumMap enumMap = new EnumMap(Season.class);
enumMap.put(Season.SUMMER , "夏日炎炎");
enumMap.put(Season.SPRING , "春暖花開");
System.out.println(enumMap);
}
}
java.util.EnumMap
對于 Map 的常用實作類而言 , 雖然 HashMap 和 Hashtable 的實作機制幾乎一樣 ,但由于 Hashtable是一個古老的、線程安全的集合,是以 HashMap 通常比 Hashtable 要快 。
TreeMap 通常比 HashMap 、 Hashtable 要慢( 尤其在插入、删除 key-value 對時更慢),因為 TreeMap底層采用紅黑樹來管理 key-value 對(紅黑樹的每個節點就是一個 key-value 對) 。使用 TreeMap 有一個好處: TreeMap 中的 key-value 對總是處于有序狀态 ,無須專門進行排序操作 。當 TreeMap 被填充之後,就可以調用 keySetO ,取得由 key 組成的 Set,然後使用 toAηayO方法生成 key的數組,接下來使用Arrays 的 binarySearchO方法在己排序的數組中快速地查詢對象。
對于一般的應用場景 , 程式應該多考慮使用 HashMap ,因為 HashMap 正是為快速查詢設計的(HashMap 底層其實也是采用數組來存儲 key-value 對 ) 。 但如果程式需要一個總是排好序的 Map 時,則可以考慮使用 TreeMap 。
LinkedHashMap 比 HashMap 慢一 點,因為它需要維護連結清單來保持 Map 中 key-value 時的 添加順序 。
EnumMap 的性能最好 ,但它隻能使用同一個枚舉類的枚舉值作為 key 。
Java Review系列目錄 ⇐⇐Java Review (二十八、集合----- Queue 集合) Java Review (三十、集合----- 操作集合的工具類: Collections)⇒⇒ |
參考:
【1】:《Java瘋狂講義》
【2】:《Java核心技術 卷 一》
【3】:
Java技術驿站:【死磕 Java 集合】— 總結篇【4】:
方志朋的專欄:Java基礎:Java容器之HashMap【5】:
方志朋的專欄:Java基礎:JAVA Hashmap的死循環及Java8的修複【6】:
廖雪峰的官方網站:使用Properties【7】:
Java技術驿站:【死磕 Java 集合】— TreeMap源碼分析(一)【8】:
廖雪峰的官方網站:使用EnumMap