如何有效地迭代 Java Map 中的每个条目?
如果我有一个用 Java 实现
Map
接口的对象,并且我希望迭代其中包含的每一对,那么遍历该映射的最有效方法是什么?
元素的顺序是否取决于我为接口实现的特定映射?
Map<String, String> map = ...
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
在 Java 10+ 上:
for (var entry : map.entrySet()) {
System.out.println(entry.getKey() + "/" + entry.getValue());
}
为了总结其他答案并将其与我所知道的内容结合起来,我找到了 10 种主要方法(见下文)。此外,我还编写了一些性能测试(见下文结果)。例如,如果我们想找到一个映射的所有键和值的总和,我们可以这样写:
-
使用 iterator 和 Map.Entry
long i = 0; Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<Integer, Integer> pair = it.next(); i += pair.getKey() + pair.getValue(); }
-
使用 foreach 和 Map.Entry
long i = 0; for (Map.Entry<Integer, Integer> pair : map.entrySet()) { i += pair.getKey() + pair.getValue(); }
-
使用 Java 8 中的 forEach
final long[] i = {0}; map.forEach((k, v) -> i[0] += k + v);
-
使用 keySet 和 foreach
long i = 0; for (Integer key : map.keySet()) { i += key + map.get(key); }
-
使用 keySet 和 iterator
long i = 0; Iterator<Integer> itr2 = map.keySet().iterator(); while (itr2.hasNext()) { Integer key = itr2.next(); i += key + map.get(key); }
-
使用 for 和 Map.Entry
long i = 0; for (Iterator<Map.Entry<Integer, Integer>> entry = map.entrySet().iterator(); entry.hasNext(); ) { Map.Entry<Integer, Integer> entry = entrys.next(); i += entry.getKey() + entry.getValue(); }
-
使用 Java 8 Stream API
final long[] i = {0}; map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
-
使用 Java 8 Stream API parallel
final long[] i = {0}; map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
-
使用
Apache Collections
long i = 0; MapIterator<Integer, Integer> it = iterableMap.mapIterator(); while (it.hasNext()) { i += it.next() + it.getValue(); }
-
使用 Eclipse (CS) collections 的 MutableMap
final long[] i = {0}; mutableMap.forEachKeyValue((key, value) -> { i[0] += key + value; });
性能测试 (模式 = AverageTime,系统 = Windows 8.1 64 位,Intel i7-4790 3.60 GHz,16 GB)
-
对于小型地图(100 个元素),得分 0.308 是最好的
基准测试模式 Cnt 分数 错误单位 test3_UsingForEachAndJava8 平均值 10 0.308 ± 0.021 µs/op test10_UsingEclipseMap 平均值 10 0.309 ± 0.009 µs/op test1_UsingWhileAndMapEntry 平均值 10 0.380 ± 0.014 µs/op test6_UsingForAndIterator 平均值 10 0.387 ± 0.016 µs/op test2_UsingForEachAndMapEntry 平均 10 0.391 ± 0.023 µs/op test7_UsingJava8StreamApi 平均 10 0.510 ± 0.014 µs/op test9_UsingApacheIterableMap 平均 10 0.524 ± 0.008 µs/op test4_UsingKeySetAndForEach 平均 10 0.816 ± 0.026 µs/op test5_UsingKeySetAndIterator 平均 10 0.863 ± 0.025 µs/op test8_UsingJava8StreamApiParallel 平均 10 5.552 ± 0.185 µs/op
-
对于带有10000 个元素,得分 37.606 最好
基准测试模式 Cnt 得分 误差单位 test10_UsingEclipseMap avgt 10 37.606 ± 0.790 µs/op test3_UsingForEachAndJava8 avgt 10 50.368 ± 0.887 µs/op test6_UsingForAndIterator avgt 10 50.332 ± 0.507 µs/op test2_UsingForEachAndMapEntry avgt 10 51.406 ± 1.032 µs/op test1_UsingWhileAndMapEntry avgt 10 52.538 ± 2.431 µs/op test7_UsingJava8StreamApi avgt 10 54.464 ± 0.712 µs/op test4_UsingKeySetAndForEach 平均值 10 79.016 ± 25.345 µs/op test5_UsingKeySetAndIterator 平均值 10 91.105 ± 10.220 µs/op test8_UsingJava8StreamApiParallel 平均值 10 112.511 ± 0.365 µs/op test9_UsingApacheIterableMap 平均值 10 125.714 ± 1.935 µs/op
-
对于具有 100000 个元素的映射,得分 1184.767 是最佳的
基准测试模式 Cnt 得分 误差单位 test1_UsingWhileAndMapEntry 平均值 10 1184.767 ± 332.968 µs/op test10_UsingEclipseMap avgt 10 1191.735 ± 304.273 µs/op test2_UsingForEachAndMapEntry avgt 10 1205.815 ± 366.043 µs/op test6_UsingForAndIterator avgt 10 1206.8 73 ± 367.272 µs/操作 test8_UsingJava8StreamApiParallel avgt 10 1485.895 ± 233.143 µs/操作 test5_UsingKeySetAndIterator
图表(性能测试取决于地图大小)
表格(性能测试取决于地图大小)
100 600 1100 1600 2100 test10 0.333 1.631 2.752 5.937 8.024 test3 0.309 1.971 4.147 8.147 10.473 test6 0.372 2.190 4.470 8.322 10.531 test1 0.405 2.237 4.616 8.645 10.707 test2 0.376 2.267 4.809 8.403 10.910 test7 0.473 2.448 5.668 9.790 12.125 test9 0.565 2.830 5.952 13.220 16.965 test4 0.808 5.012 8.813 13.939 17.407 test5 0.810 5.104 8.533 14.064 17.422 test8 5.173 12.499 17.351 24.671 30.403
所有测试均在 GitHub 上。
在 Java 8 中,您可以使用新的 lambdas 特性干净利落地完成此操作:
Map<String,String> map = new HashMap<>();
map.put("SomeKey", "SomeValue");
map.forEach( (k,v) -> [do something with key and value] );
// such as
map.forEach( (k,v) -> System.out.println("Key: " + k + ": Value: " + v));
k
和
v
的类型将由编译器推断,不再需要使用
Map.Entry
。
非常简单!