entrySet()遍历键值对最常用且推荐,避免重复查表;仅需key或value时选keySet()或values();遍历时删除须用Iterator.remove();多线程优先用ConcurrentHashMap。
entrySet() 遍历键值对最常用也最推荐绝大多数场景下,要同时访问 key 和 value,entrySet() 是最优选择。它返回的是 Set,每个 Map.Entry 封装了一组键值对,避免了重复查表开销。
常见错误是先用 keySet() 获取 key,再循环调用 map.get(key) —— 对 HashMap 影响不大,但对 TreeMap 或自定义 Map 实现可能触发多次查找或排序逻辑,性能打折。
实操建议:
entrySet(),简洁安全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); }
keySet() 或 values()
如果业务逻辑只需要 key(比如判断是否存在某 key、批量做 key 格式校验),用 keySet();如果只关心 value(比如统计所有 value 的总和、找最大值),用 values()。两者都比 entrySet() 少一层封装,内存和 CPU 开销略小。
注意点:
values() 返回的是 Collection,不保证顺序(除非底层 Map 有序,如 LinkedHashMap 或 TreeMap)keySet() 和 values() 返回的集合是 Map 的“视图”,修改它们会直接影响原 Map(例如调用 keySet().remove("k1") 等价于 map.remove("k1"))values() 反查 key —— 没有映射关系保障,多个 key 可能对应相同 valueIterator
当遍历过程中要删除元素,或 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())
forEach() 和 stream() 写法看起来更函数式,但实际有隐含成本和限制:
map.forEach((k, v) -> {...}) 底层仍是 entrySet() + 增强 for,语法糖而已,不提升性能map.entrySet().stream()... 会额外创建 Stream 对象和中间操作链,小数据量无感,大数据量或高频调用时 GC 压力明显break 或 continue,想提前退出只能抛异常(不推荐)或改用传统循环parallelStream())对 Map 遍历收益极低,还可能因 key 分布不均导致负载不均真正适合用 lambda 的场景:一次性的、带过滤/映射/聚合的简单操作,比如 map.values().stream().filter(Objects::nonNull).mapToInt(Integer::intValue).sum()。