HashMap 遍歷從大的方向來說,可分爲以下 4 類:
1.For Each 方式遍歷;
2.迭代器(Iterator)方式遍歷;
3.Lambda 表達式遍歷(JDK 1.8+);
4.Streams API 遍歷(JDK 1.8+)。
但每種類型下又有不同的實現方式,因此具體的遍歷方式又可以分爲以下 7 種:
1.使用 For Each EntrySet 的方式進行遍歷;
2.使用 For Each KeySet 的方式進行遍歷;
3.使用迭代器(Iterator)EntrySet 的方式進行遍歷;
4.使用迭代器(Iterator)KeySet 的方式進行遍歷;
5.使用 Lambda 表達式的方式進行遍歷;
6.使用 Streams API 單線程的方式進行遍歷;
7.使用 Streams API 多線程的方式進行遍歷。
接下來我們來看每種遍歷方式的具體實現代碼:
方式一 使用For Each EntrySet 這是最常見的並且在大多數情況下也是最可取的遍歷方式。在鍵值都需要時使用。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
方法二 使用For Each KeySet 在for-each循環中遍歷keys或values。
如果只需要map中的鍵或者值,你可以通過keySet或values來實現遍歷,而不是用entrySet。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍歷map中的鍵
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key);
}
//遍歷map中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
該方法比entrySet遍歷在性能上稍好(快了10%),而且代碼更加乾淨。
**方法三 使用Iterator EntrySet遍歷,即使用泛型 **
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
**方法四 使用Iterator KeySet 遍歷 **
Map map = new HashMap();
Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer)entry.getKey();
Integer value = (Integer)entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
你也可以在keySet和values上應用同樣的方法。
該種方式看起來冗餘卻有其優點所在。首先,在老版本java中這是惟一遍歷map的方式。另一個好處是,你可以在遍歷時調用iterator.remove()來刪除entries,另兩個方法則不能。根據javadoc的說明,如果在for-each遍歷中嘗試使用此方法,結果是不可預測的。
從性能方面看,該方法類同於for-each遍歷(即方法二)的性能。
方法五 使用 Lambda 表達式的方式進行遍歷
Map map = new HashMap();
map.forEach((key, value) -> {
System.out.println(key);
System.out.println(value);
});
方法六 使用 Streams API 單線程的方式進行遍歷
Map map = new HashMap();
map.entrySet().stream().forEach((entry) -> {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
});
方法七 使用 Streams API 多線程的方式進行遍歷
Map map = new HashMap();
map.entrySet().parallelStream().forEach((entry) -> {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
});
總結
性能測試,比較一下這 7 種循環的性能。
因爲 parallelStream 爲多線程版本性能一定是最好的,所以就不參與測試了,其他 6 個方法的測試結果如下:
其中 Units 爲 ns/op 意思是執行完成時間(單位爲納秒),而 Score 列爲平均執行時間, ± 符號表示誤差。從以上結果可以看出,兩個 entrySet 的性能相近,並且執行速度最快,接下來是 stream ,然後是兩個 keySet,性能最差的是 KeySet 。
所有優先使用的順序爲:
Streams API 多線程 > 迭代器(Iterator)EntrySet > For Each EntrySet > For Each KeySet > 迭代器(Iterator)KeySet > Lambda 表達式 > Streams API 單線程