[譯]Top 9 Questions About Java Maps

本篇原文

通常來說,Map是一個由鍵值對組成的數(shù)據(jù)結(jié)構(gòu),并且在一個 map 中,每一個 key 都只能出現(xiàn)一次.這篇文章總結(jié)了九個關(guān)于如何使用 Java Map 和它的實(shí)現(xiàn)類的問題.后文中為了方便,Map的類型都使用的是泛型.因此,這里僅會使用Map而不是指定數(shù)據(jù)類型的 Map,但是你仍然可以假設(shè)KV 都是可排序的.

0.Convert a Map to a List

在 Java 中Map 接口提供了三種 collection views:key set,value set,和 key-value set.這些都可以通過使用構(gòu)造方法或者allAll()被轉(zhuǎn)換成List.下面這個代碼段展示了如何從一個 map 中構(gòu)造一個ArrayList

// key list
List keyList = new ArrayList(map.keySet());
// value list
List valueList = new ArrayList(map.values());
// key-value list
List entryList = new ArrayList(map.entrySet());

1.Iterate over a Map

迭代訪問鍵值對是一種常見的遍歷 map 的方式.在 Java 中,這樣的鍵值對是存在 mapp 的Map.Entry 內(nèi)部類中.這個內(nèi)部類返回一個 key-value set,因此遍歷一個 map 最有效的方式是

for(Entry entry: map.entrySet()) {
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

同樣可以使用迭代器(Iterator)

Iterator itr = map.entrySet().iterator();
while(itr.hasNext()) {
  Entry entry = itr.next();
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

2.Sort a Map on the keys

如何給map 的 key 進(jìn)行排序也是一個被頻繁提起的問題.一個有效的方式是將 Map.Entry 放在 list 之中,然后用 comparaotr 接口對 list 進(jìn)行排序

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {

  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getKey().compareTo(e2.getKey());
  }

});

另外一個方式是使用SortedMap.如果要使用SortMap 就得要保證所有的 key 都得實(shí)現(xiàn)Comparable 接口,或者接收一個Comparator.

SortMap 的一個實(shí)現(xiàn)類是TreeMap.它的構(gòu)造方法可以接收一個Comparator.下面這段代碼展示了如何將一個普通的 Map 轉(zhuǎn)成一個可排序的 Map.

SortedMap sortedMap = new TreeMap(new Comparator() {

  @Override
  public int compare(K k1, K k2) {
    return k1.compareTo(k2);
  }

});
sortedMap.putAll(map);

3.Sort a Map on the values

將map 放在list 中排序同樣也適用于這種情況,不過這次調(diào)用的是Entry.getValue()方法,來看一下下面這段代碼:

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {

  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getValue().compareTo(e2.getValue());
  }

});

同樣的,仍然可以使用一個 sorted map 來解決這個問題,不過這僅僅適用于所有的 value 也都是唯一的情況.在這種情況下,你可以將鍵值對倒轉(zhuǎn)使用,就像 key=valuevalue=key.但是這種情形具有太強(qiáng)的限制性,所以這里并不推薦使用

4.Initialize a static/immutable Map

如果你希望你的 map 在代碼中保持不變,將它變成一個 immutable map 是一個很不錯的方法.這種防御性的代碼技術(shù)將為你創(chuàng)造一個線程安全并且不會改變的 map.

我們可以通過使用 static 代碼塊來創(chuàng)建一個 static/immutable map,就像下面這樣

public class Test {

  private static final Map map;
  static {
    map = new HashMap();
    map.put(1, "one");
    map.put(2, "two");
  }
}

這段代碼的問題是即使 map 被聲明成了一個靜態(tài)常量,仍然可以通過Test.map.put這種方式對 map 進(jìn)行修改.因此這并不是真正的 immutable.為了能創(chuàng)建一個真的不可變的 map,我們需要一個額外的匿名類,并且將這個匿名類復(fù)制到一個不可修改的 map 中,就像下面這段代碼一樣

public class Test {

  private static final Map map;
  static {
    Map aMap = new HashMap();
    aMap.put(1, "one");
    aMap.put(2, "two");
    map = Collections.unmodifiableMap(aMap);
  }
}

如果對這段代碼調(diào)用Test.map.put方法,將會拋出一個UnsupportedOperationException 異常.

Guava 庫提供了幾種不同的方式來創(chuàng)建 static/immutable collection.這里就不展開講解,如果感興趣,可以查看他們的git.

5.Difference between HashMap,TreeMap,and Hashtable

Java 中的Map 有三個主要的實(shí)現(xiàn)類,它們分別是:HashMap,TreeMap,和Hashtable.下面列舉一下這幾個實(shí)現(xiàn)類最主要的區(qū)別:

1.迭代的順序.HashMapHashtable是不保證取值的順序就是存值的順序的,甚至無法保證每次遍歷時的順序(即分兩次遍歷,返回順序也可能不同).但是TreeMap的迭代順序是依據(jù) key 的自然排序或者是給 key 一個Comparator.

2.key-value權(quán)限.HashMap允許null keynull value,Hashtable即不允許null key也不允許null value.而TreeMap則是分情況的,如果它使用的是自然排序或者它的comparator不允許null key,則使用空值會拋出異常.

3.Synchronized.只有Hashtable是同步的,其他的都不是.因此,如果不需要線程安全的話,推薦使用HashMap.

如果想了解更多,可以查看這篇文章.

6.A Map with reverse view/lookup

有些時候,我們需要一個key-keyset,這意味著 map 的 value 就像 key 一樣,是不重復(fù)的.這種約束創(chuàng)造了一種可反轉(zhuǎn)的 map.所以我們可以通過 value 來找 key.這種數(shù)據(jù)結(jié)構(gòu)被稱為雙向映射(bidirectional map),但是不幸的是,JDK 并不支持這種 map.

不過 Apache 和 Guava 提供的工具類都有這種雙向映射的實(shí)現(xiàn),分別是BidiMapBiMap.它們都強(qiáng)制約束了 key 與 value 之間一對一的關(guān)系

7.Shallow copy of a Map

Java 提供的大多數(shù) Map 的實(shí)現(xiàn)類都提供了一個復(fù)制其他map的構(gòu)造方法.但是這個復(fù)制的過程不是線程安全的.這意味著當(dāng)一個線程在復(fù)制一個 map,另外一個可能在修改它的結(jié)構(gòu).為了避免這種不同步的意外發(fā)生,map 需要提前使用Collections.synchronizedMap方法

Map copiedMap = Collections.synchronizedMap(map);

另外一個淺拷貝的方法是使用clone().然鵝甚至 Java 集合框架的設(shè)計(jì)者都不建議使用這種方式

I often provide a public clone method on concrete classes because people expect it. ... It's a shame that Cloneable is broken, but it happens. ... Cloneable is a weak spot, and I think people should be aware of its limitations.

正是由于這個原因,這里就不講如何通過clone()方法來實(shí)現(xiàn)map 的淺復(fù)制了.

8.Create an empty Map

如果要創(chuàng)建一個 immutable map,可以使用

map = Collections.emptyMap();

否則,可以使用任一一個實(shí)現(xiàn)類,比如

map = new HashMap();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末审洞,一起剝皮案震驚了整個濱河市东且,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌精刷,老刑警劉巖部凑,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件息堂,死亡現(xiàn)場離奇詭異电谣,居然都是意外死亡尼酿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門殉挽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拓巧,你說我怎么就攤上這事斯碌。” “怎么了肛度?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵傻唾,是天一觀的道長。 經(jīng)常有香客問我,道長冠骄,這世上最難降的妖魔是什么伪煤? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮凛辣,結(jié)果婚禮上抱既,老公的妹妹穿的比我還像新娘。我一直安慰自己扁誓,他們只是感情好防泵,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝗敢,像睡著了一般捷泞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寿谴,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天锁右,我揣著相機(jī)與錄音,去河邊找鬼讶泰。 笑死咏瑟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的峻厚。 我是一名探鬼主播响蕴,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼惠桃!你這毒婦竟也來了浦夷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤辜王,失蹤者是張志新(化名)和其女友劉穎劈狐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呐馆,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肥缔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了汹来。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片续膳。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖收班,靈堂內(nèi)的尸體忽然破棺而出坟岔,到底是詐尸還是另有隱情,我是刑警寧澤摔桦,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布社付,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鸥咖。R本人自食惡果不足惜燕鸽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啼辣。 院中可真熱鬧啊研,春花似錦、人聲如沸熙兔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽住涉。三九已至麸锉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舆声,已是汗流浹背花沉。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留媳握,地道東北人碱屁。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像蛾找,于是被迫代替她去往敵國和親娩脾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法打毛,類相關(guān)的語法柿赊,內(nèi)部類的語法,繼承相關(guān)的語法幻枉,異常的語法碰声,線程的語...
    子非魚_t_閱讀 31,664評論 18 399
  • 一胰挑、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對于byte類型而言...
    龍貓小爺閱讀 4,268評論 0 16
  • Java SE 基礎(chǔ): 封裝、繼承椿肩、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨(dú)立的整體瞻颂,并盡...
    Jayden_Cao閱讀 2,112評論 0 8
  • HashMap 是 Java 面試必考的知識點(diǎn),面試官從這個小知識點(diǎn)就可以了解我們對 Java 基礎(chǔ)的掌握程度郑象。網(wǎng)...
    野狗子嗷嗷嗷閱讀 6,669評論 9 107
  • Part1 “趙怡蘸朋!過來,回來了扣唱!” 我應(yīng)聲,循著同學(xué)的聲音,繞過巨大的圓形廢水處理池噪沙,穿過粗空氣廊的拱形鋼化玻璃...
    d8dc987602b4閱讀 498評論 11 12