JavaHashMap遍歷實踐,看看不同方式的效能如何
在原始記憶中,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
這種方法我以前幾乎不用,但方法二卻有其關鍵的優點:
- 可以通過iterator對map的元素進行刪除。方法一同樣。
- 效能優良。
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 |
可以總結如下:
- 迭代keys並搜尋values 非常低效,排名幾乎在倒數第一或第二
- For-Each迭代entries 效能最佳,但無法remove
- For-Each迭代keys和values並沒有比For-Each迭代entries 效能(大約快10%),stackoverflow上的資料也不能全部苟同
- Iterator迭代Entry 的方案顯然最適合使用,效能優良,且可以remove
參考文章
感謝您閱讀【沉默王二的部落格】,如果王二的部落格給您帶來一絲幫助或感動,我(也就是王二)將不甚榮幸。
如果您碰巧喜歡,可以留言或者私信我,這將是我鼓搗更多優秀文章的最強動力。
相關文章
- 如何從效能角度選擇陣列的遍歷方式陣列
- 如何遍歷 HashMap,遍歷HashMap 的 5 種最佳方式HashMap
- HashMap 的 7 種遍歷方式與效能分析HashMap
- 樹的遍歷方式
- JS遍歷物件的方式JS物件
- python字串遍歷方式Python字串
- map的四種遍歷方式
- hashMap的四種遍歷方式HashMap
- php手冊 php陣列的遍歷有哪幾種方式?php陣列如何遍歷?PHP陣列
- js 遍歷陣列方式JS陣列
- Map集合的四種遍歷方式
- python字典的四種遍歷方式Python
- Map集合&&Map集合的不同遍歷【keySet()&&entrySet()】
- JS中遍歷陣列、物件的方式JS陣列物件
- 遍歷資料夾的幾種方式
- 遍歷PHP陣列的6種方式PHP陣列
- Java遍歷Map物件的四種方式Java物件
- pandas中的遍歷方式速度對比
- Python字典的遍歷,包括key遍歷/value遍歷/item遍歷/Python
- python實現圖(基於圖的不同儲存方式)的深度優先(DFS)和廣度(BFS)優先遍歷Python
- c++遍歷陣列的多種方式C++陣列
- JS遍歷物件屬性的7種方式JS物件
- 遍歷 Dictionary,你會幾種方式?
- 如何遍歷HashMap集合?HashMap
- php陣列中常用的多種遍歷方式PHP陣列
- 【C#】-遍歷資料夾簡約的方式C#
- PHP二維關聯陣列的遍歷方式PHP陣列
- js的map遍歷和array遍歷JS
- Shell:如何遍歷包含空格的文字
- 前端技巧:遍歷陣列都有哪些方式呢?前端陣列
- 非遞迴實現先序遍歷和中序遍歷遞迴
- HashMap原始碼:聊聊Map的遍歷效能問題(一)HashMap原始碼
- java陣列如何遍歷全部的元素Java陣列
- TDictionary 的 遍歷
- 深度優先遍歷,廣度優先遍歷實現物件的深拷貝物件
- js實現深度優先遍歷和廣度優先遍歷JS
- 【JavaScript實用技巧(一)】迴圈遍歷與跳出迴圈遍歷JavaScript
- Golang的陣列初始化方式及for-range遍歷Golang陣列
- 二叉樹的遍歷實現二叉樹