1. 概述

Java 中的 Map 是一种键值对存储结构,支持通过键进行高效查找、更新和删除操作。掌握 Map 的遍历方法对很多编程任务至关重要。

本文将深入探讨 Java 中遍历 Map 的多种方式,并通过基准测试对比性能差异,帮你找到最适合业务场景的方案。

简单来说,我们可以通过 entrySet()keySet()values() 方法获取 Map 内容。由于这些方法都返回集合,遍历原理基本相通。

2. 快速了解 Map 的核心方法

在深入遍历技术前,先复习三个访问 Map 内容的关键方法:

entrySet()
返回 Map.Entry 类型的 Set 视图。通过 entry.getKey() 获取键,entry.getValue() 获取值。

keySet()
返回包含所有键的 Set 集合。

values()
返回包含所有值的 Collection(注意不是 Set)。

3. 使用 for-each + entrySet()

最经典的遍历方式:通过 entrySet() 结合增强 for 循环

long iterateUsingEnhancedForLoopAndEntrySet(Map<Integer, Integer> map) {
    long sum = 0;
    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
        sum += entry.getValue();
    }
    return sum;
}

这里先提取 Map 的条目集合,然后用传统 for-each 遍历并累加值。

4. 使用 for-each + keySet()

当需要先获取键再访问值时,keySet() 是备选方案:

long iterateUsingKeySetAndEnhanceForLoop(Map<Integer, Integer> map) {
    long sum = 0;
    for (Integer key : map.keySet()) {
        sum += map.get(key);
    }
    return sum;
}

⚠️ 注意:这种方式通过键二次获取值,性能稍差(后文有数据验证)。

5. 使用 for-each + values()

只关心值不关心键时,values() 是最直接的选择:

long iterateValuesUsingValuesMethodAndEnhanceForLoop(Map<Integer, Integer> map) {
    long sum = 0;
    for (Integer value : map.values()) {
        sum += value;
    }
    return sum;
}

直接遍历值集合,完全跳过键的操作。

6. 使用 Iterator + entrySet()

传统迭代器方式遍历 entrySet()

long iterateUsingIteratorAndEntrySet(Map<Integer, Integer> map) {
    long sum = 0;
    Iterator<Map.Entry<Integer, Integer>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<Integer, Integer> pair = iterator.next();
        sum += pair.getValue();
    }
    return sum;
}

适合需要在遍历中删除元素的场景(通过 iterator.remove())。

7. 使用 Iterator + keySet()

同样可以用迭代器遍历 keySet()

long iterateUsingIteratorAndKeySet(Map<Integer, Integer> map) {
    long sum = 0;
    Iterator iterator = map.keySet().iterator();
    while (iterator.hasNext()) {
        Integer key = iterator.next();
        sum += map.get(key);
    }
    return sum;
}

和 for-each 版本一样,存在二次取值开销。

8. 使用 Iterator + values()

迭代器遍历值集合:

long iterateUsingIteratorAndValues(Map<Integer, Integer> map) {
    long sum = 0;
    Iterator iterator = map.values().iterator();
    while (iterator.hasNext()) {
        Integer value = iterator.next();
        sum += value;
    }
    return sum;
}

纯值遍历场景的迭代器实现。

9. 使用 forEach + Lambda

Java 8 引入的 forEach() 配合 Lambda 表达式:

void iterateUsingLambdaAndForEach(Map<Integer, Integer> map) {
    map.forEach((k, v) -> LOGGER.info("Key: {}, Value: {}", k, v));
}

适用场景:对每个条目执行独立操作(如日志记录)。

不推荐场景:聚合操作(如求和):

long iterateValuesUsingLambdaAndForEach(Map<Integer, Integer> map) {
    AtomicLong sum = new AtomicLong(0);
    map.values().forEach(v -> sum.addAndGet(v));
    return sum.get();
}

聚合操作建议用 Stream API(后文详述)。

10. 使用 Stream API

Java 8 的 Stream API 为数据处理提供强大能力:

long iterateUsingStreamAPIAndEntrySet(Map<Integer, Integer> map) {
    return map.entrySet()
      .stream()
      .mapToLong(Map.Entry::getValue)
      .sum();
}

同样支持 keySet() 版本:

long iterateUsingStreamAPIAndKeySet(Map<Integer, Integer> map) {
    return map.keySet()
      .stream()
      .mapToLong(map::get)
      .sum();
}

并行处理场景用 parallelStream()

long iterateMapUsingParallelStreamApi(Map<Integer, Integer> map) throws IOException {
    return map.entrySet()
        .parallelStream()
        .mapToLong(Map.Entry::getValue)
        .sum();
}

⚠️ 注意:并行流有额外开销,小数据量时可能适得其反。

11. 遍历 Eclipse Collections 的 MutableMap

Eclipse Collections 提供高性能集合实现:

long iterateEclipseMap(MutableMap<Integer, Integer> mutableMap) throws IOException {
    AtomicLong sum = new AtomicLong(0);
    mutableMap.forEachKeyValue((key, value) -> {
        sum.addAndGet(value);
    });
    return sum.get();
}

通过 forEachKeyValue() 直接操作键值对。

12. 遍历 Apache Commons 的 IterableMap

Apache Commons CollectionsIterableMap 使用专用迭代器:

long iterateUsingMapIteratorApacheCollection(IterableMap<Integer, Integer> map) {
    long sum = 0;
    MapIterator<Integer, Integer> iterate = map.mapIterator();
    while (iterate.hasNext()) {
        iterate.next();
        sum += iterate.getValue();
    }
    return sum;
}

MapIterator 支持直接获取键值,无需二次查找。

13. 性能对比

使用 JMH 进行基准测试:

13.1 测试环境配置

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 10, timeUnit = TimeUnit.MICROSECONDS)
@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.MICROSECONDS)
public class MapIterationBenchmark {
    
    @Param({"100","1000","10000","100000"})
    public int size; 
    MapIteration mapIteration = new MapIteration();
    Map<Integer, Integer> map;
    IterableMap<Integer, Integer> iterableMap;
    MutableMap<Integer, Integer> mutableMap;
    // ...
}

初始化方法:

@Setup(Level.Trial)
public void setup() {
    map = new HashMap<>();
    iterableMap = new HashedMap<>();
    mutableMap = UnifiedMap.newMap();
    for (int i = 0; i < size; i++) {
        map.put(i, i);
        iterableMap.put(i, i);
        mutableMap.put(i, i);
    }
}

13.2 测试结果(10万元素)

方法 平均耗时 (秒) 误差范围
parallelStream() 0.001 ±0.001
Iterator+entrySet() 0.002 ±0.002
Stream+entrySet() 0.002 ±0.001
forEach+Lambda 0.002 ±0.001
for-each+entrySet() 0.003 ±0.005
for-each+values() 0.003 ±0.004
for-each+keySet() 0.003 ±0.003
Eclipse Collections 0.003 ±0.003
Iterator+keySet() 0.004 ±0.003
Apache Commons 0.004 ±0.010

关键发现

  1. 并行流 (parallelStream) 在大数据量时表现最佳
  2. ✅ **entrySet() 始终优于 keySet()**(避免二次取值)
  3. Apache Commons 迭代器性能最差且误差最大
  4. ⚠️ 误差率较高:测试结果需谨慎解读

14. 总结

本文系统梳理了 Java 中遍历 Map 的 12 种方法,核心结论如下:

  • 首选方案entrySet() + for-each(代码简洁且高效)
  • 聚合操作:优先使用 Stream API(链式调用更优雅)
  • 大数据量:考虑 parallelStream()(注意线程安全)
  • 特殊场景
    • 需要删除元素 → 用 Iterator
    • 只关心值 → 用 values()
    • 使用第三方库 → 优先 Eclipse Collections

💡 踩坑提示:避免在循环中用 keySet() + map.get(),性能损失可达 20%!

选择遍历方式时,优先考虑代码可读性,性能瓶颈出现时再针对性优化。


原始标题:Iterate Over a Map in Java | Baeldung