JavaHashMap遍歷實踐,看看不同方式的效能如何

沉默王二發表於2016-03-04
版權宣告:歡迎轉載,請註明沉默王二原創。 https://blog.csdn.net/qing_gee/article/details/50801937

在原始記憶中,Java HashMap遍歷,無非是for each或者iterator,但至於在遍歷時效能如何,優缺點如何,泛泛而不得知。對於這樣的基礎問題,對於王二(Java程式設計6年,幸好我的方向不是程式設計)我來說,似乎羞於提及,但事實證明,我還必須“積矽步”。

①方法一、iterator迭代keys並搜尋values

該種方法是我使用最頻繁的,沒有之一,詳見如下程式碼:

Map<Integer, Integer> map = new HashMap<Integer, Integer>();

addMap(map);

long t1 = System.currentTimeMillis();
Iterator<Integer> keys = map.keySet().iterator();

while (keys.hasNext()) {
    Integer key = keys.next();
    Integer value = map.get(key);

    keys.remove();
}

long t2 = System.currentTimeMillis();
System.out.println("map.keySet().iterator()耗時:" + (t2 - t1));

以前的我看來,該種方法使用起來相當簡潔,通過iterator遍歷出來keys,然後通過key從map中獲取對應的value,似乎也非常接地氣。但是,弊端就在於

它更慢更低效,通過key得到value值更耗時(這個方法在所有實現map介面的map中比方法#1慢20%-200%)。如果你安裝了FindBugs,它將檢測並警告你這是一個低效的迭代。這個方法應該避免

看到這條資訊,我是覺得有點唐突,怎麼原來最喜歡的一種map遍歷方式竟然如此low,簡直讓人失望。後面我會對花費的效能時間做一個統計,稍候關注。

②方法二、Iterator迭代Entry

這種方法我以前幾乎不用,但方法二卻有其關鍵的優點:

  1. 可以通過iterator對map的元素進行刪除。方法一同樣。
  2. 效能優良。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
addMap(map);

long t3 = System.currentTimeMillis();
Iterator<Entry<Integer, Integer>> entrys = map.entrySet().iterator();

while (entrys.hasNext()) {
    Entry<Integer, Integer> entry = entrys.next();
    Integer key = entry.getKey();
    Integer value = entry.getValue();

    entrys.remove();
}

long t4 = System.currentTimeMillis();
System.out.println(" map.entrySet().iterator()耗時:" + (t4 - t3));

通過“map.entrySet().iterator()”獲得map的entry物件,然後通過getKey,getValue進行key和value值得獲取,非常的直白和實用。

③方法三:For-Each迭代keys和values

for each一個侷限是不同remove map中的元素,但遍歷map還是非常好的。

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
addMap(map);

long t5 = System.currentTimeMillis();
for (Integer key : map.keySet()) {
}

for (Integer value : map.values()) {
}
long t6 = System.currentTimeMillis();
System.out.println("for each map.keySet()、map.values()耗時:" + (t6 - t5));

不過這裡,王二有話要說,按照stackoverflow上所說,該種方法要比接下來說的第四種方法“For-Each迭代entries”效能更好(大約快10%),但在我的實踐中並非如此,這種方法反而比第四種“For-Each迭代entries”慢得多。

④方法四:For-Each迭代entries

Map<Integer, Integer> map = new HashMap<Integer, Integer>();
addMap(map);

long t7 = System.currentTimeMillis();
for (Entry<Integer, Integer> entry : map.entrySet()) {
    Integer key = entry.getKey();
    Integer value = entry.getValue();
}
long t8 = System.currentTimeMillis();
System.out.println("for each map.entrySet()耗時:" + (t8 - t7));

這種方法就不多做介紹了。

⑤效能時間表

次序 iterator迭代keys並搜尋values Iterator迭代Entry For-Each迭代keys和values For-Each迭代entries
1 耗時:37 耗時:32 耗時:39 耗時:13
2 耗時:29 耗時:18 耗時:32 耗時:15
3 耗時:50 耗時:57 耗時:39 耗時:21
4 耗時:47 耗時:31 耗時:39 耗時:14

可以總結如下:

  1. 迭代keys並搜尋values 非常低效,排名幾乎在倒數第一或第二
  2. For-Each迭代entries 效能最佳,但無法remove
  3. For-Each迭代keys和values並沒有比For-Each迭代entries 效能(大約快10%),stackoverflow上的資料也不能全部苟同
  4. Iterator迭代Entry 的方案顯然最適合使用,效能優良,且可以remove

參考文章


感謝您閱讀【沉默王二的部落格】,如果王二的部落格給您帶來一絲幫助或感動,我(也就是王二)將不甚榮幸。
如果您碰巧喜歡,可以留言或者私信我,這將是我鼓搗更多優秀文章的最強動力。


相關文章