天天看點

Java Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 Map 實作類的性能分析

文章目錄

圖一:Map集合結構圖

Java Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 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 組

Java Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 Map 實作類的性能分析

從圖二中可以看出 ,如果把 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繼承示意圖

Java Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 Map 實作類的性能分析

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 Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 Map 實作類的性能分析
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繼承示意圖

Java Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 Map 實作類的性能分析

正如 Set 接口派生出 SortedSet 子接口, SortedSet 接口有一個 TreeSet 實作類一樣, Map 接口也派生出一個 SortedMap 子接口, SortedMap 接口也有一個 TreeMap 實作類 。

TreeMap 就是 一個紅黑樹資料結構 , 每個 key-value 對即作為紅黑樹的一個節點 。 TreeMap 存儲key-value 對(節點)時, 需要根據 key 對節點進行排序 。 TreeMap 可以保證所有的 key-value 對處于有序狀态。

圖六:TreeMap存儲資料結構示意圖

Java Review (二十九、集合----- Map 集合)HashMap 和 Hashtable 實作類LinkedHashMap 實作類Properties 類SortedMap 接口和 TreeMap 實作類IdentityHashMap 實作類EnumMap 實作類各 Map 實作類的性能分析

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 。

參考:

【1】:《Java瘋狂講義》

【2】:《Java核心技術 卷 一》

【3】:

Java技術驿站:【死磕 Java 集合】— 總結篇

【4】:

方志朋的專欄:Java基礎:Java容器之HashMap

【5】:

方志朋的專欄:Java基礎:JAVA Hashmap的死循環及Java8的修複

【6】:

廖雪峰的官方網站:使用Properties

【7】:

Java技術驿站:【死磕 Java 集合】— TreeMap源碼分析(一)

【8】:

廖雪峰的官方網站:使用EnumMap