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 个方法的测试结果如下:

java 遍历对象 去重复 java怎么遍历对象_迭代器


其中 Units 为 ns/op 意思是执行完成时间(单位为纳秒),而 Score 列为平均执行时间, ± 符号表示误差。从以上结果可以看出,两个 entrySet 的性能相近,并且执行速度最快,接下来是 stream ,然后是两个 keySet,性能最差的是 KeySet 。

所有优先使用的顺序为:
Streams API 多线程 > 迭代器(Iterator)EntrySet > For Each EntrySet > For Each KeySet > 迭代器(Iterator)KeySet > Lambda 表达式 > Streams API 单线程