map.entry 是遍历 map 时唯一能同时安全获取 key 和 value 的方式,比 keyset() + get() 更高效且避免重复哈希查找与并发读取过期值。

Map.Entry 是遍历 Map 时唯一能同时拿到 key 和 value 的安全方式
直接用 entrySet() 拿到 Map.Entry 集合,比分别调用 keySet() 再查 get() 快且安全。JVM 对 Entry 实现做了优化,比如 HashMap.Node 本身就是一个 Map.Entry,遍历时不额外构造对象。
- 常见错误:写
for (String k : map.keySet()) { v = map.get(k); }—— 多一次哈希查找,对ConcurrentHashMap还可能读到过期值 - 正确姿势:用
for (Map.Entry<k v> e : map.entrySet())</k>,e.getKey()和e.getValue()都是 O(1) 字段访问 - 注意:不要在遍历中调用
e.setValue()修改结构(如TreeMap会破坏排序),但修改值本身通常允许(取决于具体实现)
用 Map.Entry 替代匿名内部类或 Lambda 中的重复 key 查找
在 stream() 或 forEach() 场景下,用 map.entrySet().stream() 能避免闭包捕获或反复 get(),尤其当 key 是复杂对象、hashCode() 开销大时更明显。
- 反例:
map.keySet().stream().map(k -> k + "=" + map.get(k))—— 每次都触发完整查找逻辑 - 正例:
map.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()) -
TreeMap下还保证顺序;LinkedHashMap保持插入序;而keySet().stream()不承诺顺序
自定义 Map 实现里必须正确实现 Map.Entry.equals() 和 hashCode()
很多手写缓存或装饰器 Map 会返回包装后的 Entry,如果没重写 equals(),会导致 remove(new AbstractMap.SimpleEntry(k,v)) 失败——因为默认比较的是引用,不是 key/value 内容。
- 标准要求:
e1.equals(e2)当且仅当e1.getKey().equals(e2.getKey()) && e1.getValue() == null ? e2.getValue() == null : e1.getValue().equals(e2.getValue()) - 别漏掉 null 安全:value 为 null 时不能直接调
.equals(),得先判空 - 若重写了
equals()却没同步改hashCode(),放进HashSet<map.entry></map.entry>就会出问题
Entry 的泛型擦除陷阱:不能靠 Entry 类型推导 key/value 实际类型
Java 泛型擦除后,Map.Entry 在运行时就是裸类型,反射或序列化时容易丢类型信息。比如用 Gson 直接序列化 entrySet(),出来的 JSON 可能没有字段名,只有数组。
- 常见现象:
new TypeToken<set>>(){}.getType()</set>在 Gson 里仍需手动注册Map.Entry的适配器 - 调试时打印
e.getClass()往往看到HashMap.Node或TreeMap.Entry,不是你写的泛型类名 - 想保留类型?老实用
new AbstractMap.SimpleEntry(k, v)构造,它明确实现了泛型方法,但注意它是不可变的
Entry 的可变性边界:它允许改 value,但改完是否影响 Map 结构,完全取决于底层实现。比如 ImmutableMap 的 Entry 会抛 UnsupportedOperationException,而 HashMap 允许。不看文档直接 setValue() 就跑,很容易在换 Map 实现时崩。Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











