標籤:
Map以按鍵/數值對的形式儲存資料,和數組很類似,在數組中存在的索引,它們本身也是對象。
Map的介面
Map---實現Map
Map.Entry--Map的內部類,描寫敘述Map中的按鍵/數值對。
SortedMap---擴充Map,使按鍵保持升序排列
關於怎麼使用,通常是選擇Map的子類,而不直接用Map類。
以下以HashMap為例。
public static void main(String args[])
{
HashMap hashmap = new HashMap();
hashmap.put("Item0", "Value0");
hashmap.put("Item1", "Value1");
hashmap.put("Item2", "Value2");
hashmap.put("Item3", "Value3");
Set set = hashmap.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()
{
Map.Entry mapentry = (Map.Entry) iterator.next();
System.out.println(mapentry.getkey() + "/" + mapentry.getValue());
}
}
注意,這裡Map的按鍵必須是唯一的,比方說不能有兩個按鍵都為null。
假設用過它,就會知道它的用處了。
又比方:
Map<String, Order> map = new HashMap<String, Order>();
map.put("Order", (Order) obj);
資料:
Collection容器中包括Set和List介面,Set中又包括HashSet,List中包括LinkedList和ArrayList;單獨的Map介面中僅僅有HashMap。
java.util 中的集合類包括 Java 中某些最經常使用的類。 最經常使用的集合類是 List 和 Map。 List 的詳細實現包括 ArrayList 和 Vector,它們是可變大小的列表,比較適合構建、儲存和操作不論什麼類型對象的元素列表。 List 適用於按數值索引訪問元素的情形,當中的資料有順序且能夠反覆。而Set中資料無順序且不能夠反覆。
Map 提供了一個更通用的元素儲存方法。 Map 集合類用於儲存元素對(稱作“鍵”和“值”),當中每一個鍵映射到一個值。 從概念上而言,您能夠將 List 看作是具有數值鍵的 Map。 而實際上,除了 List 和 Map 都在定義 java.util 中外,兩者並沒有直接的聯絡。本文將著重介紹核心 Java 發行套件中附帶的 Map,同一時候還將介紹怎樣採用或實現更適用於您應用程式特定資料的專用 Map。
瞭解 Map 介面和方法
Java 核心類中有非常多提前定義的 Map 類。 在介紹詳細實現之前,我們先介紹一下 Map 介面本身,以便瞭解全部實現的共同點。 Map 介面定義了四種類型的方法,每一個 Map 都包括這些方法。 以下,我們從兩個普通的方法(表 1)開始對這些方法加以介紹。
表 1: 覆蓋的方法。 我們將這 Object 的這兩個方法覆蓋,以正確比較 Map 對象的等價性。
equals(Object o) |
比較指定對象與此 Map 的等價性 |
hashCode() |
返回此 Map 的雜湊碼 |
Map 構建
Map 定義了幾個用於插入和刪除元素的變換方法(表 2)。
表 2: Map 更新方法: 能夠更改 Map 內容。
clear() |
從 Map 中刪除全部映射 |
remove(Object key) |
從 Map 中刪除鍵和關聯的值 |
put(Object key, Object value) |
將指定值與指定鍵相關聯 |
clear() |
從 Map 中刪除全部映射 |
putAll(Map t) |
將指定 Map 中的全部映射拷貝到此 map |
雖然您可能注意到,縱然如果忽略構建一個須要傳遞給 putAll() 的 Map 的開銷,使用 putAll() 通常也並不比使用大量的 put() 調用更有效率,但 putAll() 的存在一點也不稀奇。 這是由於,putAll() 除了迭代 put() 所啟動並執行將每一個索引值對加入到 Map 的演算法以外,還須要迭代所傳遞的 Map 的元素。 但應注意,putAll() 在加入全部元素之前能夠正確調整 Map 的大小,因此如果您未親自調整 Map 的大小(我們將對此進行簡介),則 putAll() 可能比預期的更有效。
查看 Map
迭代 Map 中的元素不存在直接了當的方法。 假設要查詢某個 Map 以瞭解其哪些元素滿足特定查詢,或假設要迭代其全部元素(不管原因怎樣),則您首先須要擷取該 Map 的“視圖”。 有三種可能的視圖(參見表 3)
- 全部索引值對 — 參見 entrySet()
- 全部鍵 — 參見 keySet()
- 全部值 — 參見 values()
前兩個視圖均返回 Set 對象,第三個視圖返回 Collection 對象。 就這兩種情況而言,問題到這裡並沒有結束,這是由於您無法直接迭代 Collection 對象或 Set 對象。要進行迭代,您必須獲得一個 Iterator 對象。 因此,要迭代 Map 的元素,必須進行比較煩瑣的編碼
Iterator keyValuePairs = aMap.entrySet().iterator();Iterator keys = aMap.keySet().iterator();Iterator values = aMap.values().iterator();
值 得注意的是,這些對象(Set、Collection 和 Iterator)實際上是基礎 Map 的視圖,而不是包括全部元素的副本。 這使它們的使用效率非常高。 還有一方面,Collection 或 Set 對象的 toArray() 方法卻建立包括 Map 全部元素的數組對象,因此除了確實須要使用數組中元素的情形外,其效率並不高。
我執行了一個小測試(隨附檔案裡的 Test1),該測試使用了 HashMap,並使用下面兩種方法對迭代 Map 元素的開銷進行了比較:
int mapsize = aMap.size();Iterator keyValuePairs1 = aMap.entrySet().iterator();for (int i = 0; i < mapsize; i++){ Map.Entry entry = (Map.Entry) keyValuePairs1.next(); Object key = entry.getKey(); Object value = entry.getValue(); ...}Object[] keyValuePairs2 = aMap.entrySet().toArray();for (int i = 0; i < rem; i++) {{ Map.Entry entry = (Map.Entry) keyValuePairs2[i]; Object key = entry.getKey();
Profilers in Oracle JDeveloper Oracle JDeveloper 包括一個嵌入的監測器,它測量記憶體和已耗用時間,使您可以高速識別代碼中的瓶頸。 我曾使用 Jdeveloper 的運行監測器監測 HashMap 的 containsKey() 和 containsValue() 方法,並不是常快發現 containsKey() 方法的速度比 containsValue() 方法慢非常多(實際上要慢幾個數量級!)。 (參見圖 1 和圖 2,以及隨附檔案裡的 Test2 類)。 |
Object value = entry.getValue(); ...}
此 測試使用了兩種測量方法: 一種是測量迭代元素的時間,還有一種測量使用 toArray 調用建立數組的其它開銷。 第一種方法(忽略建立數組所需的時間)表明,使用已從 toArray 調用中建立的數組迭代元素的速度要比使用 Iterator 的速度大約快 30%-60%。 但假設將使用 toArray 方法建立數組的開銷包括在內,則使用 Iterator 實際上要快 10%-20%。 因此,假設因為某種原因要建立一個集合元素的數組而非迭代這些元素,則應使用該數組迭代元素。 但假設您不須要此中間數組,則不要建立它,而是使用 Iterator 迭代元素。
表 3: 返回視圖的 Map 方法: 使用這些方法返回的對象,您能夠遍曆 Map 的元素,還能夠刪除 Map 中的元素。
entrySet() |
返回 Map 中所包括映射的 Set 視圖。 Set 中的每一個元素都是一個 Map.Entry 對象,能夠使用 getKey() 和 getValue() 方法(另一個 setValue() 方法)訪問後者的鍵元素和值元素 |
keySet() |
返回 Map 中所包括鍵的 Set 視圖。 刪除 Set 中的元素還將刪除 Map 中對應的映射(鍵和值) |
values() |
返回 map 中所包括值的 Collection 視圖。 刪除 Collection 中的元素還將刪除 Map 中對應的映射(鍵和值) |
訪問元素
表 4 中列出了 Map 訪問方法。Map 通常適合按鍵(而非按值)進行訪問。 Map 定義中沒有規定這肯定是真的,但通常您能夠期望這是真的。 比如,您能夠期望 containsKey() 方法與 get() 方法一樣快。 還有一方面,containsValue() 方法非常可能須要掃描 Map 中的值,因此它的速度可能比較慢。
表 4: Map 訪問和測試方法: 這些方法檢索有關 Map 內容的資訊但不更改 Map 內容。
get(Object key) |
返回與指定鍵關聯的值 |
containsKey(Object key) |
假設 Map 包括指定鍵的映射,則返回 true |
containsValue(Object value) |
假設此 Map 將一個或多個鍵映射到指定值,則返回 true |
isEmpty() |
假設 Map 不包括鍵-值對應,則返回 true |
size() |
返回 Map 中的鍵-值對應的數目 |
對使用 containsKey() 和 containsValue() 遍曆 HashMap 中全部元素所需時間的測試表明,containsValue() 所需的時間要長非常多。 實際上要長几個數量級! (參見圖 1 和圖 2,以及隨附檔案裡的 Test2)。 因此,假設 containsValue() 是應用程式中的效能問題,它將非常快顯現出來,並可以通過監測您的應用程式輕鬆地將其識別。 這樣的情況下,我相信您可以想出一個有效替換方法來實現 containsValue() 提供的等效功能。 但假設想不出辦法,則一個可行的解決方式是再建立一個 Map,並將第一個 Map 的全部值作為鍵。 這樣,第一個 Map 上的 containsValue() 將成為第二個 Map 上更有效 containsKey()。
在地圖中使用Java