entryset()遍历应直接用entry.getvalue()而非map.get(key),避免重复哈希查找;keyset()+get()适合只读key场景;stream遍历性能较低,小数据量优先用for循环。

entrySet() 遍历时别直接 get(key) 查值
用 entrySet() 的本意是避免重复哈希查找,但很多人写成 map.get(entry.getKey()),这就白费了——相当于把 O(1) 操作退化成 O(1) + 一次哈希计算 + 一次数组寻址。
正确做法是直接用 entry.getValue():
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey(); // ✅
Integer value = entry.getValue(); // ✅
// 不要再写 map.get(key)
}
- 适用于需要同时访问 key 和 value 的场景,比如日志打印、条件过滤后构造新 Map
- 对
HashMap、LinkedHashMap效率最高;TreeMap下也比两次遍历快 - 注意:
entrySet()返回的是视图,修改 entry 的 value 会反映到原 map(仅限支持的实现,如HashMap),但不能调用entry.setValue(null)来删键
keySet() + get() 在小 Map 或只读 key 场景更自然
如果逻辑只依赖 key(比如检查是否存在、做字符串拼接、传给其他方法),用 keySet() 更直觉,且代码意图清晰。
但要注意:每次 map.get(k) 都是一次哈希查找。在大 Map 中高频调用会拖慢速度。
立即学习“Java免费学习笔记(深入)”;
- 适合场景:key 数量少(
-
keySet()是轻量视图,不复制数据;但get()成本恒定,和 size 无关 - 若需频繁取 value,优先考虑
entrySet();若只处理 key,keySet()更安全(避免误改 value)
forEach() + Lambda 容易踩空指针和并发修改
Map.forEach() 看似简洁,但底层调用的是 entrySet().forEach(),所以它和显式遍历 entrySet() 性能几乎一致。问题出在副作用上。
常见错误:
- 在 lambda 里调用
map.remove(key)→ 抛ConcurrentModificationException - lambda 参数为
(k, v),但没判空,而 map 允许 null key/value → 运行时报NullPointerException - 想在 forEach 里 break 或 continue → Lambda 不支持,只能用 return 模拟,可读性差
示例(危险):
map.forEach((k, v) -> {
if (v > 100) map.remove(k); // ❌ 并发修改异常
});
安全替代:用传统 for 循环 + Iterator.remove(),或收集待删 key 后批量删。
Stream API 遍历 Map 的性能代价常被低估
用 map.entrySet().stream()... 写法优雅,但默认是顺序流,且每步操作(filter、map)都新建对象、触发装箱/拆箱、额外函数调用开销。
- 5000 以内元素,
for循环比 Stream 快 2–5 倍;10 万级差距更明显 - 除非你真需要并行处理(
parallelStream())、链式转换、或配合 Optional/Collectors 做聚合,否则没必要上 Stream - 注意:
Collectors.toMap()可能抛IllegalStateException(重复 key),而传统循环可以主动跳过或覆盖
简单映射建议手写循环,复杂数据转换再考虑 Stream —— 别为了“看起来函数式”牺牲可读和性能。
entrySet 是最通用的选择,但要不要用它,取决于你到底动不动 value;keySet 看似简单,一不小心就多查几次 hash;forEach 表面省事,背后全是隐式约束。这些不是语法差异,是运行时行为差异。










