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 单线程