17370845950

在Java里如何遍历Map集合_JavaMap遍历方式解析
entrySet()遍历键值对最常用且推荐,避免重复查表;仅需key或value时选keySet()或values();遍历时删除须用Iterator.remove();多线程优先用ConcurrentHashMap。

entrySet() 遍历键值对最常用也最推荐

绝大多数场景下,要同时访问 key 和 value,entrySet() 是最优选择。它返回的是 Set>,每个 Map.Entry 封装了一组键值对,避免了重复查表开销。

常见错误是先用 keySet() 获取 key,再循环调用 map.get(key) —— 对 HashMap 影响不大,但对 TreeMap 或自定义 Map 实现可能触发多次查找或排序逻辑,性能打折。

实操建议:

  • 用增强 for 循环直接遍历 entrySet(),简洁安全
  • 若需修改 value,可调用 entry.setValue(newVal)(仅对支持修改的 Map 有效)
  • 不要在遍历时直接调用 map.remove(key),会抛 ConcurrentModificationException;应使用 Iterator.remove()

for (Map.Entry entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); System.out.println(key + "=" + value); }

只遍历 key 或只遍历 value 时选 keySet()values()

如果业务逻辑只需要 key(比如判断是否存在某 key、批量做 key 格式校验),用 keySet();如果只关心 value(比如统计所有 value 的总和、找最大值),用 values()。两者都比 entrySet() 少一层封装,内存和 CPU 开销略小。

注意点:

  • values() 返回的是 Collection,不保证顺序(除非底层 Map 有序,如 LinkedHashMapTreeMap
  • keySet()values() 返回的集合是 Map 的“视图”,修改它们会直接影响原 Map(例如调用 keySet().remove("k1") 等价于 map.remove("k1")
  • 不能通过 values() 反查 key —— 没有映射关系保障,多个 key 可能对应相同 value

需要并发安全或边遍历边删除时必须用 Iterator

当遍历过程中要删除元素,或 Map 被多线程共享(哪怕只是读多写少),就不能依赖增强 for 循环——它底层隐式使用 Iterator,但没暴露 remove 方法,且无法应对并发修改。

正确做法是显式获取 Iterator,并在循环中调用其 remove() 方法:

Iterator> iter = map.entrySet().iterator();
while (iter.hasNext()) {
    Map.Entry entry = iter.next();
    if (entry.getValue() < 0) {
        iter.remove(); // 安全删除
    }
}

其他注意事项:

  • ConcurrentHashMap,可放心用增强 for 遍历(不会抛 ConcurrentModificationException),但删除仍需用 computeIfPresent()remove() 方法,不能依赖 Iterator.remove()
  • 若在多线程环境下仅读取,优先用 ConcurrentHashMap 替代 synchronized(new HashMap())

Lambda 方式(Java 8+)适合简单过滤或转换,别滥用

forEach()stream() 写法看起来更函数式,但实际有隐含成本和限制:

  • map.forEach((k, v) -> {...}) 底层仍是 entrySet() + 增强 for,语法糖而已,不提升性能
  • map.entrySet().stream()... 会额外创建 Stream 对象和中间操作链,小数据量无感,大数据量或高频调用时 GC 压力明显
  • lambda 表达式里无法 breakcontinue,想提前退出只能抛异常(不推荐)或改用传统循环
  • Stream 并行化(parallelStream())对 Map 遍历收益极低,还可能因 key 分布不均导致负载不均

真正适合用 lambda 的场景:一次性的、带过滤/映射/聚合的简单操作,比如 map.values().stream().filter(Objects::nonNull).mapToInt(Integer::intValue).sum()

遍历 Map 看似简单,但 keySet/get、entrySet、Iterator、lambda 四种方式背后涉及迭代器契约、fail-fast 机制、视图集合语义、并发模型等细节。最容易被忽略的是「遍历时删除」和「多线程读写」这两个边界情况——出问题往往不是语法错,而是对 Map 实现类行为理解偏差。