开发者问题收集

如何有效地迭代 Java Map 中的每个条目?

2008-09-05
3408726

如果我有一个用 Java 实现 Map 接口的对象,并且我希望迭代其中包含的每一对,那么遍历该映射的最有效方法是什么?

元素的顺序是否取决于我为接口实现的特定映射?

3个回答
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());
}
ScArcher2
2008-09-05

为了总结其他答案并将其与我所知道的内容结合起来,我找到了 10 种主​​要方法(见下文)。此外,我还编写了一些性能测试(见下文结果)。例如,如果我们想找到一个映射的所有键和值的总和,我们可以这样写:

  1. 使用 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();
    }
    
  2. 使用 foreach Map.Entry

    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
    i += pair.getKey() + pair.getValue();
    }
    
  3. 使用 Java 8 中的 forEach

    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    
  4. 使用 keySet foreach

    long i = 0;
    for (Integer key : map.keySet()) {
    i += key + map.get(key);
    }
    
  5. 使用 keySet iterator

    long i = 0;
    Iterator<Integer> itr2 = map.keySet().iterator();
    while (itr2.hasNext()) {
    Integer key = itr2.next();
    i += key + map.get(key);
    }
    
  6. 使用 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();
    }
    
  7. 使用 Java 8 Stream API

    final long[] i = {0};
    map.entrySet().stream().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  8. 使用 Java 8 Stream API parallel

    final long[] i = {0};
    map.entrySet().stream().parallel().forEach(e -> i[0] += e.getKey() + e.getValue());
    
  9. 使用 Apache Collections

    long i = 0;
    MapIterator<Integer, Integer> it = iterableMap.mapIterator();
    while (it.hasNext()) {
    i += it.next() + it.getValue();
    }
    
  10. 使用 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)

  1. 对于小型地图(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
    
  2. 对于带有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
    
  3. 对于具有 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 上。

Slava Vedenin
2016-02-22

在 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));

kv 的类型将由编译器推断,不再需要使用 Map.Entry

非常简单!

The Coordinator
2013-10-21