Map以按鍵/數(shù)值對(duì)的形式存儲(chǔ)數(shù)據(jù),和數(shù)組非常相似,在數(shù)組中存在的索引,它們本身也是對(duì)象。
Map的接口
Map---實(shí)現(xiàn)Map
Map.Entry--Map的內(nèi)部類(lèi),描述Map中的按鍵/數(shù)值對(duì)。
SortedMap---擴(kuò)展Map,使按鍵保持升序排列
關(guān)于怎么使用,一般是選擇Map的子類(lèi),而不直接用Map類(lèi)。
下面以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的按鍵必須是唯一的,比如說(shuō)不能有兩個(gè)按鍵都為null。
如果用過(guò)它,就會(huì)知道它的用處了。
資料:
java.util 中的集合類(lèi)包含 Java 中某些最常用的類(lèi)。 最常用的集合類(lèi)是 List 和 Map。 List 的具體實(shí)現(xiàn)包括 ArrayList 和 Vector,它們是可變大小的列表,比較適合構(gòu)建、存儲(chǔ)和操作任何類(lèi)型對(duì)象的元素列表。 List 適用于按數(shù)值索引訪(fǎng)問(wèn)元素的情形。
Map 提供了一個(gè)更通用的元素存儲(chǔ)方法。 Map 集合類(lèi)用于存儲(chǔ)元素對(duì)(稱(chēng)作“鍵”和“值”),其中每個(gè)鍵映射到一個(gè)值。 從概念上而言,您可以將 List 看作是具有數(shù)值鍵的 Map。 而實(shí)際上,除了 List 和 Map 都在定義 java.util 中外,兩者并沒(méi)有直接的聯(lián)系。本文將著重介紹核心 Java 發(fā)行套件中附帶的 Map,同時(shí)還將介紹如何采用或?qū)崿F(xiàn)更適用于您應(yīng)用程序特定數(shù)據(jù)的專(zhuān)用 Map。
了解 Map 接口和方法
Java 核心類(lèi)中有很多預(yù)定義的 Map 類(lèi)。 在介紹具體實(shí)現(xiàn)之前,我們先介紹一下 Map 接口本身,以便了解所有實(shí)現(xiàn)的共同點(diǎn)。 Map 接口定義了四種類(lèi)型的方法,每個(gè) Map 都包含這些方法。 下面,我們從兩個(gè)普通的方法(表 1)開(kāi)始對(duì)這些方法加以介紹。
表 1: 覆蓋的方法。 我們將這 Object 的這兩個(gè)方法覆蓋,以正確比較 Map 對(duì)象的等價(jià)性。
equals(Object o) | 比較指定對(duì)象與此 Map 的等價(jià)性 |
hashCode() | 返回此 Map 的哈希碼 |
Map 構(gòu)建
Map 定義了幾個(gè)用于插入和刪除元素的變換方法(表 2)。
表 2: Map 更新方法:可以更改 Map 內(nèi)容。
clear() | 從 Map 中刪除所有映射 |
remove(Object key) | 從 Map 中刪除鍵和關(guān)聯(lián)的值 |
put(Object key, Object value) | 將指定值與指定鍵相關(guān)聯(lián) |
clear() | 從 Map 中刪除所有映射 |
putAll(Map t) | 將指定 Map 中的所有映射復(fù)制到此 map |
盡管您可能注意到,縱然假設(shè)忽略構(gòu)建一個(gè)需要傳遞給 putAll() 的 Map 的開(kāi)銷(xiāo),使用 putAll() 通常也并不比使用大量的 put() 調(diào)用更有效率,但 putAll() 的存在一點(diǎn)也不稀奇。 這是因?yàn)椋?span lang="EN-US">putAll() 除了迭代 put() 所執(zhí)行的將每個(gè)鍵值對(duì)添加到 Map 的算法以外,還需要迭代所傳遞的 Map 的元素。 但應(yīng)注意,putAll() 在添加所有元素之前可以正確調(diào)整 Map 的大小,因此如果您未親自調(diào)整 Map 的大?。ㄎ覀儗?duì)此進(jìn)行簡(jiǎn)單介紹),則 putAll() 可能比預(yù)期的更有效。
查看 Map
迭代 Map 中的元素不存在直接了當(dāng)?shù)姆椒ā?如果要查詢(xún)某個(gè) Map 以了解其哪些元素滿(mǎn)足特定查詢(xún),或如果要迭代其所有元素(無(wú)論原因如何),則您首先需要獲取該 Map 的“視圖”。 有三種可能的視圖(參見(jiàn)表 3)
- 所有鍵值對(duì) — 參見(jiàn) entrySet()
- 所有鍵 — 參見(jiàn) keySet()
- 所有值 — 參見(jiàn) values()
前兩個(gè)視圖均返回 Set 對(duì)象,第三個(gè)視圖返回 Collection 對(duì)象。 就這兩種情況而言,問(wèn)題到這里并沒(méi)有結(jié)束,這是因?yàn)槟鸁o(wú)法直接迭代 Collection 對(duì)象或 Set 對(duì)象。要進(jìn)行迭代,您必須獲得一個(gè) Iterator 對(duì)象。 因此,要迭代 Map 的元素,必須進(jìn)行比較煩瑣的編碼
Iterator keyValuePairs = aMap.entrySet().iterator();Iterator keys = aMap.keySet().iterator();Iterator values = aMap.values().iterator();
值得注意的是,這些對(duì)象(Set、Collection 和 Iterator)實(shí)際上是基礎(chǔ) Map 的視圖,而不是包含所有元素的副本。 這使它們的使用效率很高。 另一方面,Collection 或 Set 對(duì)象的 toArray() 方法卻創(chuàng)建包含 Map 所有元素的數(shù)組對(duì)象,因此除了確實(shí)需要使用數(shù)組中元素的情形外,其效率并不高。
我運(yùn)行了一個(gè)小測(cè)試(隨附文件中的 Test1),該測(cè)試使用了 HashMap,并使用以下兩種方法對(duì)迭代 Map 元素的開(kāi)銷(xiāo)進(jìn)行了比較:
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 包含一個(gè)嵌入的監(jiān)測(cè)器,它測(cè)量?jī)?nèi)存和執(zhí)行時(shí)間,使您能夠快速識(shí)別代碼中的瓶頸。 我曾使用 Jdeveloper 的執(zhí)行監(jiān)測(cè)器監(jiān)測(cè) HashMap 的 containsKey() 和 containsValue() 方法,并很快發(fā)現(xiàn) containsKey() 方法的速度比 containsValue() 方法慢很多(實(shí)際上要慢幾個(gè)數(shù)量級(jí)?。?。 (參見(jiàn)圖 1 和圖 2,以及隨附文件中的 Test2 類(lèi))。 |
Object value = entry.getValue(); ...}
此測(cè)試使用了兩種測(cè)量方法: 一種是測(cè)量迭代元素的時(shí)間,另一種測(cè)量使用 toArray 調(diào)用創(chuàng)建數(shù)組的其他開(kāi)銷(xiāo)。 第一種方法(忽略創(chuàng)建數(shù)組所需的時(shí)間)表明,使用已從 toArray 調(diào)用中創(chuàng)建的數(shù)組迭代元素的速度要比使用 Iterator 的速度大約快 30%-60%。 但如果將使用 toArray 方法創(chuàng)建數(shù)組的開(kāi)銷(xiāo)包含在內(nèi),則使用 Iterator 實(shí)際上要快 10%-20%。 因此,如果由于某種原因要?jiǎng)?chuàng)建一個(gè)集合元素的數(shù)組而非迭代這些元素,則應(yīng)使用該數(shù)組迭代元素。 但如果您不需要此中間數(shù)組,則不要?jiǎng)?chuàng)建它,而是使用 Iterator 迭代元素。
表 3: 返回視圖的 Map 方法: 使用這些方法返回的對(duì)象,您可以遍歷 Map 的元素,還可以刪除 Map 中的元素。
entrySet() | 返回 Map 中所包含映射的 Set 視圖。 Set 中的每個(gè)元素都是一個(gè) Map.Entry 對(duì)象,可以使用 getKey() 和 getValue() 方法(還有一個(gè) setValue() 方法)訪(fǎng)問(wèn)后者的鍵元素和值元素 |
keySet() | 返回 Map 中所包含鍵的 Set 視圖。 刪除 Set 中的元素還將刪除 Map 中相應(yīng)的映射(鍵和值) |
values() | 返回 map 中所包含值的 Collection 視圖。 刪除 Collection 中的元素還將刪除 Map 中相應(yīng)的映射(鍵和值) |
訪(fǎng)問(wèn)元素
表 4 中列出了 Map 訪(fǎng)問(wèn)方法。Map 通常適合按鍵(而非按值)進(jìn)行訪(fǎng)問(wèn)。 Map 定義中沒(méi)有規(guī)定這肯定是真的,但通常您可以期望這是真的。例如,您可以期望 containsKey() 方法與 get() 方法一樣快。另一方面,containsValue() 方法很可能需要掃描 Map 中的值,因此它的速度可能比較慢。
表 4: Map 訪(fǎng)問(wèn)和測(cè)試方法:這些方法檢索有關(guān) Map 內(nèi)容的信息但不更改 Map 內(nèi)容。
get(Object key) | 返回與指定鍵關(guān)聯(lián)的值 |
containsKey(Object key) | 如果 Map 包含指定鍵的映射,則返回 true |
containsValue(Object value) | 如果此 Map 將一個(gè)或多個(gè)鍵映射到指定值,則返回 true |
isEmpty() | 如果 Map 不包含鍵-值映射,則返回 true |
size() | 返回 Map 中的鍵-值映射的數(shù)目 |
對(duì)使用 containsKey() 和 containsValue() 遍歷 HashMap 中所有元素所需時(shí)間的測(cè)試表明,containsValue() 所需的時(shí)間要長(zhǎng)很多。 實(shí)際上要長(zhǎng)幾個(gè)數(shù)量級(jí)! (參見(jiàn)圖 1 和圖 2,以及隨附文件中的 Test2)。 因此,如果 containsValue() 是應(yīng)用程序中的性能問(wèn)題,它將很快顯現(xiàn)出來(lái),并可以通過(guò)監(jiān)測(cè)您的應(yīng)用程序輕松地將其識(shí)別。 這種情況下,我相信您能夠想出一個(gè)有效的替換方法來(lái)實(shí)現(xiàn) containsValue() 提供的等效功能。 但如果想不出辦法,則一個(gè)可行的解決方案是再創(chuàng)建一個(gè) Map,并將第一個(gè) Map 的所有值作為鍵。 這樣,第一個(gè) Map 上的 containsValue() 將成為第二個(gè) Map 上更有效的 containsKey()。