小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Java Map 集合類簡(jiǎn)介

 smoking_boy 2005-09-29

java.util 中的集合類包含 Java 中某些最常用的類。 最常用的集合類是 List 和 Map。 List 的具體實(shí)現(xiàn)包括 ArrayList 和 Vector,它們是可變大小的列表,比較適合構(gòu)建、存儲(chǔ)和操作任何類型對(duì)象的元素列表。 List 適用于按數(shù)值索引訪問元素的情形。

Map 提供了一個(gè)更通用的元素存儲(chǔ)方法。 Map 集合類用于存儲(chǔ)元素對(duì)(稱作“鍵”和“值”),其中每個(gè)鍵映射到一個(gè)值。 從概念上而言,您可以將 List 看作是具有數(shù)值鍵的 Map。 而實(shí)際上,除了 List 和 Map 都在定義 java.util 中外,兩者并沒有直接的聯(lián)系。本文將著重介紹核心 Java 發(fā)行套件中附帶的 Map,同時(shí)還將介紹如何采用或?qū)崿F(xiàn)更適用于您應(yīng)用程序特定數(shù)據(jù)的專用 Map。

了解 Map 接口和方法

Java 核心類中有很多預(yù)定義的 Map 類。 在介紹具體實(shí)現(xiàn)之前,我們先介紹一下 Map 接口本身,以便了解所有實(shí)現(xiàn)的共同點(diǎn)。 Map 接口定義了四種類型的方法,每個(gè) Map 都包含這些方法。 下面,我們從兩個(gè)普通的方法(表 1)開始對(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 的開銷,使用 putAll() 通常也并不比使用大量的 put() 調(diào)用更有效率,但 putAll() 的存在一點(diǎn)也不稀奇。 這是因?yàn)椋琾utAll() 除了迭代 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ù)姆椒ā?如果要查詢某個(gè) Map 以了解其哪些元素滿足特定查詢,或如果要迭代其所有元素(無論原因如何),則您首先需要獲取該 Map 的“視圖”。 有三種可能的視圖(參見表 3

  • 所有鍵值對(duì) — 參見 entrySet()
  • 所有鍵 — 參見 keySet()
  • 所有值 — 參見 values()

前兩個(gè)視圖均返回 Set 對(duì)象,第三個(gè)視圖返回 Collection 對(duì)象。 就這兩種情況而言,問題到這里并沒有結(jié)束,這是因?yàn)槟鸁o法直接迭代 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 元素的開銷進(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í)?。?(參見圖 1圖 2,以及隨附文件中的 Test2 類)。

Object value = entry.getValue(); ... }
此測(cè)試使用了兩種測(cè)量方法: 一種是測(cè)量迭代元素的時(shí)間,另一種測(cè)量使用 toArray 調(diào)用創(chuàng)建數(shù)組的其他開銷。 第一種方法(忽略創(chuàng)建數(shù)組所需的時(shí)間)表明,使用已從 toArray 調(diào)用中創(chuàng)建的數(shù)組迭代元素的速度要比使用 Iterator 的速度大約快 30%-60%。 但如果將使用 toArray 方法創(chuàng)建數(shù)組的開銷包含在內(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() 方法)訪問后者的鍵元素和值元素
keySet() 返回 Map 中所包含鍵的 Set 視圖。 刪除 Set 中的元素還將刪除 Map 中相應(yīng)的映射(鍵和值)
values() 返回 map 中所包含值的 Collection 視圖。 刪除 Collection 中的元素還將刪除 Map 中相應(yīng)的映射(鍵和值)

訪問元素

表 4 中列出了 Map 訪問方法。Map 通常適合按鍵(而非按值)進(jìn)行訪問。 Map 定義中沒有規(guī)定這肯定是真的,但通常您可以期望這是真的。 例如,您可以期望 containsKey() 方法與 get() 方法一樣快。 另一方面,containsValue() 方法很可能需要掃描 Map 中的值,因此它的速度可能比較慢。

表 4: Map 訪問和測(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í)! (參見圖 1圖 2,以及隨附文件中的 Test2)。 因此,如果 containsValue() 是應(yīng)用程序中的性能問題,它將很快顯現(xiàn)出來,并可以通過監(jiān)測(cè)您的應(yīng)用程序輕松地將其識(shí)別。 這種情況下,我相信您能夠想出一個(gè)有效的替換方法來實(shí)現(xiàn) containsValue() 提供的等效功能。 但如果想不出辦法,則一個(gè)可行的解決方案是再創(chuàng)建一個(gè) Map,并將第一個(gè) Map 的所有值作為鍵。 這樣,第一個(gè) Map 上的 containsValue() 將成為第二個(gè) Map 上更有效的 containsKey()。

圖 1
圖 1: 使用 JDeveloper 創(chuàng)建并運(yùn)行 Map 測(cè)試類

圖 2
圖 2: 在 JDeveloper 中使用執(zhí)行監(jiān)測(cè)器進(jìn)行的性能監(jiān)測(cè)查出應(yīng)用程序中的瓶頸

核心 Map

Java 自帶了各種 Map 類。 這些 Map 類可歸為三種類型:

  1. 通用 Map,用于在應(yīng)用程序中管理映射,通常在 java.util 程序包中實(shí)現(xiàn)
    • HashMap
    • Hashtable
    • Properties
    • LinkedHashMap
    • IdentityHashMap
    • TreeMap
    • WeakHashMap
    • ConcurrentHashMap
  2. 專用 Map,您通常不必親自創(chuàng)建此類 Map,而是通過某些其他類對(duì)其進(jìn)行訪問
    • java.util.jar.Attributes
    • javax.print.attribute.standard.PrinterStateReasons
    • java.security.Provider
    • java.awt.RenderingHints
    • javax.swing.UIDefaults
  3. 一個(gè)用于幫助實(shí)現(xiàn)您自己的 Map 類的抽象類
    • AbstractMap

內(nèi)部哈希: 哈希映射技術(shù)

幾乎所有通用 Map 都使用哈希映射。 這是一種將元素映射到數(shù)組的非常簡(jiǎn)單的機(jī)制,您應(yīng)了解哈希映射的工作原理,以便充分利用 Map。

哈希映射結(jié)構(gòu)由一個(gè)存儲(chǔ)元素的內(nèi)部數(shù)組組成。 由于內(nèi)部采用數(shù)組存儲(chǔ),因此必然存在一個(gè)用于確定任意鍵訪問數(shù)組的索引機(jī)制。 實(shí)際上,該機(jī)制需要提供一個(gè)小于數(shù)組大小的整數(shù)索引值。 該機(jī)制稱作哈希函數(shù)。 在 Java 基于哈希的 Map 中,哈希函數(shù)將對(duì)象轉(zhuǎn)換為一個(gè)適合內(nèi)部數(shù)組的整數(shù)。 您不必為尋找一個(gè)易于使用的哈希函數(shù)而大傷腦筋: 每個(gè)對(duì)象都包含一個(gè)返回整數(shù)值的 hashCode() 方法。 要將該值映射到數(shù)組,只需將其轉(zhuǎn)換為一個(gè)正值,然后在將該值除以數(shù)組大小后取余數(shù)即可。 以下是一個(gè)簡(jiǎn)單的、適用于任何對(duì)象的 Java 哈希函數(shù)

int hashvalue = Maths.abs(key.hashCode()) % table.length;

(% 二進(jìn)制運(yùn)算符(稱作模)將左側(cè)的值除以右側(cè)的值,然后返回整數(shù)形式的余數(shù)。)

實(shí)際上,在 1.4 版發(fā)布之前,這就是各種基于哈希的 Map 類所使用的哈希函數(shù)。 但如果您查看一下代碼,您將看到

int hashvalue = (key.hashCode() & 0x7FFFFFFF) % table.length;

它實(shí)際上是使用更快機(jī)制獲取正值的同一函數(shù)。 在 1.4 版中,HashMap 類實(shí)現(xiàn)使用一個(gè)不同且更復(fù)雜的哈希函數(shù),該函數(shù)基于 Doug Lea 的 util.concurrent 程序包(稍后我將更詳細(xì)地再次介紹 Doug Lea 的類)。

圖 3
圖 3: 哈希工作原理

該圖介紹了哈希映射的基本原理,但我們還沒有對(duì)其進(jìn)行詳細(xì)介紹。 我們的哈希函數(shù)將任意對(duì)象映射到一個(gè)數(shù)組位置,但如果兩個(gè)不同的鍵映射到相同的位置,情況將會(huì)如何? 這是一種必然發(fā)生的情況。 在哈希映射的術(shù)語中,這稱作沖突。 Map 處理這些沖突的方法是在索引位置處插入一個(gè)鏈接列表,并簡(jiǎn)單地將元素添加到此鏈接列表。 因此,一個(gè)基于哈希的 Map 的基本 put() 方法可能如下所示

public Object put(Object key, Object value) {
  //我們的內(nèi)部數(shù)組是一個(gè) Entry 對(duì)象數(shù)組
  //Entry[] table;

  //獲取哈希碼,并映射到一個(gè)索引
  int hash = key.hashCode();
  int index = (hash & 0x7FFFFFFF) % table.length;

  //循環(huán)遍歷位于 table[index] 處的鏈接列表,以查明
  //我們是否擁有此鍵項(xiàng) — 如果擁有,則覆蓋它
  for (Entry e = table[index] ; e != null ; e = e.next) {
    //必須檢查鍵是否相等,原因是不同的鍵對(duì)象
    //可能擁有相同的哈希
    if ((e.hash == hash) && e.key.equals(key)) {
      //這是相同鍵,覆蓋該值
      //并從該方法返回 old 值
      Object old = e.value;
      e.value = value;
      return old;
    }
  }

  //仍然在此處,因此它是一個(gè)新鍵,只需添加一個(gè)新 Entry
  //Entry 對(duì)象包含 key 對(duì)象、 value 對(duì)象、一個(gè)整型的 hash、
  //和一個(gè)指向列表中的下一個(gè) Entry 的 next Entry

  //創(chuàng)建一個(gè)指向上一個(gè)列表開頭的新 Entry,
  //并將此新 Entry 插入表中
  Entry e = new Entry(hash, key, value, table[index]);
  table[index] = e;

  return null;
}

如果看一下各種基于哈希的 Map 的源代碼,您將發(fā)現(xiàn)這基本上就是它們的工作原理。 此外,還有一些需要進(jìn)一步考慮的事項(xiàng),如處理空鍵和值以及調(diào)整內(nèi)部數(shù)組。 此處定義的 put() 方法還包含相應(yīng) get() 的算法,這是因?yàn)椴迦氚ㄋ阉饔成渌饕幍捻?xiàng)以查明該鍵是否已經(jīng)存在。 (即 get() 方法與 put() 方法具有相同的算法,但 get() 不包含插入和覆蓋代碼。) 使用鏈接列表并不是解決沖突的唯一方法,某些哈希映射使用另一種“開放式尋址”方案,本文對(duì)其不予介紹。

優(yōu)化 Hasmap

如果哈希映射的內(nèi)部數(shù)組只包含一個(gè)元素,則所有項(xiàng)將映射到此數(shù)組位置,從而構(gòu)成一個(gè)較長(zhǎng)的鏈接列表。 由于我們的更新和訪問使用了對(duì)鏈接列表的線性搜索,而這要比 Map 中的每個(gè)數(shù)組索引只包含一個(gè)對(duì)象的情形要慢得多,因此這樣做的效率很低。 訪問或更新鏈接列表的時(shí)間與列表的大小線性相關(guān),而使用哈希函數(shù)訪問或更新數(shù)組中的單個(gè)元素則與數(shù)組大小無關(guān) — 就漸進(jìn)性質(zhì)(Big-O 表示法)而言,前者為 O(n),而后者為 O(1)。 因此,使用一個(gè)較大的數(shù)組而不是讓太多的項(xiàng)聚集在太少的數(shù)組位置中是有意義的。

調(diào)整 Map 實(shí)現(xiàn)的大小

在哈希術(shù)語中,內(nèi)部數(shù)組中的每個(gè)位置稱作“存儲(chǔ)桶”(bucket),而可用的存儲(chǔ)桶數(shù)(即內(nèi)部數(shù)組的大?。┓Q作容量 (capacity)。 為使 Map 對(duì)象有效地處理任意數(shù)目的項(xiàng),Map 實(shí)現(xiàn)可以調(diào)整自身的大小。 但調(diào)整大小的開銷很大。 調(diào)整大小需要將所有元素重新插入到新數(shù)組中,這是因?yàn)椴煌臄?shù)組大小意味著對(duì)象現(xiàn)在映射到不同的索引值。 先前沖突的鍵可能不再?zèng)_突,而先前不沖突的其他鍵現(xiàn)在可能沖突。 這顯然表明,如果將 Map 調(diào)整得足夠大,則可以減少甚至不再需要重新調(diào)整大小,這很有可能顯著提高速度。

使用 1.4.2 JVM 運(yùn)行一個(gè)簡(jiǎn)單的測(cè)試,即用大量的項(xiàng)(數(shù)目超過一百萬)填充 HashMap。 表 5 顯示了結(jié)果,并將所有時(shí)間標(biāo)準(zhǔn)化為已預(yù)先設(shè)置大小的服務(wù)器模式(關(guān)聯(lián)文件中的 Test3)。 對(duì)于已預(yù)先設(shè)置大小的 JVM,客戶端和服務(wù)器模式 JVM 運(yùn)行時(shí)間幾乎相同(在放棄 JIT 編譯階段后)。 但使用 Map 的默認(rèn)大小將引發(fā)多次調(diào)整大小操作,開銷很大,在服務(wù)器模式下要多用 50% 的時(shí)間,而在客戶端模式下幾乎要多用兩倍的時(shí)間!

表 5: 填充已預(yù)先設(shè)置大小的 HashMap 與填充默認(rèn)大小的 HashMap 所需時(shí)間的比較

客戶端模式 服務(wù)器模式
預(yù)先設(shè)置的大小 100% 100%
默認(rèn)大小 294% 157%

使用負(fù)載因子

為確定何時(shí)調(diào)整大小,而不是對(duì)每個(gè)存儲(chǔ)桶中的鏈接列表的深度進(jìn)行記數(shù),基于哈希的 Map 使用一個(gè)額外參數(shù)并粗略計(jì)算存儲(chǔ)桶的密度。 Map 在調(diào)整大小之前,使用名為“負(fù)載因子”的參數(shù)指示 Map 將承擔(dān)的“負(fù)載”量,即它的負(fù)載程度。 負(fù)載因子、項(xiàng)數(shù)(Map 大?。┡c容量之間的關(guān)系簡(jiǎn)單明了:

  • 如果(負(fù)載因子)x(容量)>(Map 大?。?,則調(diào)整 Map 大小

例如,如果默認(rèn)負(fù)載因子為 0.75,默認(rèn)容量為 11,則 11 x 0.75 = 8.25,該值向下取整為 8 個(gè)元素。 因此,如果將第 8 個(gè)項(xiàng)添加到此 Map,則該 Map 將自身的大小調(diào)整為一個(gè)更大的值。 相反,要計(jì)算避免調(diào)整大小所需的初始容量,用將要添加的項(xiàng)數(shù)除以負(fù)載因子,并向上取整,例如,

  • 對(duì)于負(fù)載因子為 0.75 的 100 個(gè)項(xiàng),應(yīng)將容量設(shè)置為 100/0.75 = 133.33,并將結(jié)果向上取整為 134(或取整為 135 以使用奇數(shù))

奇數(shù)個(gè)存儲(chǔ)桶使 map 能夠通過減少?zèng)_突數(shù)來提高執(zhí)行效率。 雖然我所做的測(cè)試(關(guān)聯(lián)文件中的Test4)并未表明質(zhì)數(shù)可以始終獲得更好的效率,但理想情形是容量取質(zhì)數(shù)。 1.4 版后的某些 Map(如 HashMap 和 LinkedHashMap,而非 Hashtable 或 IdentityHashMap)使用需要 2 的冪容量的哈希函數(shù),但下一個(gè)最高 2 的冪容量由這些 Map 計(jì)算,因此您不必親自計(jì)算。

負(fù)載因子本身是空間和時(shí)間之間的調(diào)整折衷。 較小的負(fù)載因子將占用更多的空間,但將降低沖突的可能性,從而將加快訪問和更新的速度。 使用大于 0.75 的負(fù)載因子可能是不明智的,而使用大于 1.0 的負(fù)載因子肯定是不明知的,這是因?yàn)檫@必定會(huì)引發(fā)一次沖突。 使用小于 0.50 的負(fù)載因子好處并不大,但只要您有效地調(diào)整 Map 的大小,應(yīng)不會(huì)對(duì)小負(fù)載因子造成性能開銷,而只會(huì)造成內(nèi)存開銷。 但較小的負(fù)載因子將意味著如果您未預(yù)先調(diào)整 Map 的大小,則導(dǎo)致更頻繁的調(diào)整大小,從而降低性能,因此在調(diào)整負(fù)載因子時(shí)一定要注意這個(gè)問題。

選擇適當(dāng)?shù)?Map

應(yīng)使用哪種 Map? 它是否需要同步? 要獲得應(yīng)用程序的最佳性能,這可能是所面臨的兩個(gè)最重要的問題。 當(dāng)使用通用 Map 時(shí),調(diào)整 Map 大小和選擇負(fù)載因子涵蓋了 Map 調(diào)整選項(xiàng)。

以下是一個(gè)用于獲得最佳 Map 性能的簡(jiǎn)單方法

  1. 將您的所有 Map 變量聲明為 Map,而不是任何具體實(shí)現(xiàn),即不要聲明為 HashMap 或 Hashtable,或任何其他 Map 類實(shí)現(xiàn)。

    Map criticalMap = new HashMap(); //好
    
    HashMap criticalMap = new HashMap(); //差
    

    這使您能夠只更改一行代碼即可非常輕松地替換任何特定的 Map 實(shí)例。

  2. 下載 Doug Lea 的 util.concurrent 程序包 (http://gee.cs./dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html)。 將 ConcurrentHashMap 用作默認(rèn) Map。 當(dāng)移植到 1.5 版時(shí),將 java.util.concurrent.ConcurrentHashMap 用作您的默認(rèn) Map。 不要將 ConcurrentHashMap 包裝在同步的包裝器中,即使它將用于多個(gè)線程。 使用默認(rèn)大小和負(fù)載因子。
  3. 監(jiān)測(cè)您的應(yīng)用程序。 如果發(fā)現(xiàn)某個(gè) Map 造成瓶頸,則分析造成瓶頸的原因,并部分或全部更改該 Map 的以下內(nèi)容: Map 類;Map 大?。回?fù)載因子;關(guān)鍵對(duì)象 equals() 方法實(shí)現(xiàn)。 專用的 Map 的基本上都需要特殊用途的定制 Map 實(shí)現(xiàn),否則通用 Map 將實(shí)現(xiàn)您所需的性能目標(biāo)。

Map 選擇

也許您曾期望更復(fù)雜的考量,而這實(shí)際上是否顯得太容易? 好的,讓我們慢慢來。 首先,您應(yīng)使用哪種 Map? 答案很簡(jiǎn)單: 不要為您的設(shè)計(jì)選擇任何特定的 Map,除非實(shí)際的設(shè)計(jì)需要指定一個(gè)特殊類型的 Map。 設(shè)計(jì)時(shí)通常不需要選擇具體的 Map 實(shí)現(xiàn)。 您可能知道自己需要一個(gè) Map,但不知道使用哪種。 而這恰恰就是使用 Map 接口的意義所在。 直到需要時(shí)再選擇 Map 實(shí)現(xiàn) — 如果隨處使用“Map”聲明的變量,則更改應(yīng)用程序中任何特殊 Map 的 Map 實(shí)現(xiàn)只需要更改一行,這是一種開銷很少的調(diào)整選擇。 是否要使用默認(rèn)的 Map 實(shí)現(xiàn)? 我很快將談到這個(gè)問題。

同步 Map

同步與否有何差別? (對(duì)于同步,您既可以使用同步的 Map,也可以使用 Collections.synchronizedMap() 將未同步的 Map 轉(zhuǎn)換為同步的 Map。 后者使用“同步的包裝器”)這是一個(gè)異常復(fù)雜的選擇,完全取決于您如何根據(jù)多線程并發(fā)訪問和更新使用 Map,同時(shí)還需要進(jìn)行維護(hù)方面的考慮。 例如,如果您開始時(shí)未并發(fā)更新特定 Map,但它后來更改為并發(fā)更新,情況將如何? 在這種情況下,很容易在開始時(shí)使用一個(gè)未同步的 Map,并在后來向應(yīng)用程序中添加并發(fā)更新線程時(shí)忘記將此未同步的 Map 更改為同步的 Map。 這將使您的應(yīng)用程序容易崩潰(一種要確定和跟蹤的最糟糕的錯(cuò)誤)。 但如果默認(rèn)為同步,則將因隨之而來的可怕性能而序列化執(zhí)行多線程應(yīng)用程序。 看起來,我們需要某種決策樹來幫助我們正確選擇。

Doug Lea 是紐約州立大學(xué)奧斯威戈分校計(jì)算機(jī)科學(xué)系的教授。 他創(chuàng)建了一組公共領(lǐng)域的程序包(統(tǒng)稱 util.concurrent),該程序包包含許多可以簡(jiǎn)化高性能并行編程的實(shí)用程序類。 這些類中包含兩個(gè) Map,即 ConcurrentReaderHashMap 和 ConcurrentHashMap。 這些 Map 實(shí)現(xiàn)是線程安全的,并且不需要對(duì)并發(fā)訪問或更新進(jìn)行同步,同時(shí)還適用于大多數(shù)需要 Map 的情況。 它們還遠(yuǎn)比同步的 Map(如 Hashtable)或使用同步的包裝器更具伸縮性,并且與 HashMap 相比,它們對(duì)性能的破壞很小。 util.concurrent 程序包構(gòu)成了 JSR166 的基礎(chǔ);JSR166 已經(jīng)開發(fā)了一個(gè)包含在 Java 1.5 版中的并發(fā)實(shí)用程序,而 Java 1.5 版將把這些 Map 包含在一個(gè)新的 java.util.concurrent 程序包中。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多