identityhashmap 用 == 而非 equals() 判断键相等,根本原因是其设计目标为标识同一对象实例而非逻辑值相等,适用于对象图遍历、序列化等需严格区分内存地址的场景。

IdentityHashMap 用 == 而不是 equals() 判断键是否相等,根本原因在于它要表达的是“对象身份”(identity),而不是“对象值”(value)。
核心目的:区分不同实例,哪怕内容完全一样
当两个对象逻辑上相等(equals() 返回 true),但内存地址不同(== 为 false),常规 Map(如 HashMap)会把它们当成同一个 key,导致后一次 put 覆盖前一次。IdentityHashMap 反其道而行之——只认“是不是同一个对象”,不关心“长得像不像”。
- 例如:
new String("a")和new String("a")内容相同,但地址不同 → 在 IdentityHashMap 中是两个独立 key - 而字符串字面量
"a"和"a"因常量池复用同一地址 →==为 true,仍会被视为重复 key
避免 equals() 带来的不确定性与开销
很多类的 equals() 实现不可靠:可能没重写、逻辑有误、依赖外部状态、甚至抛异常。IdentityHashMap 绕过这些问题,直接走 JVM 最底层的引用比较,稳定且轻量。
- 跳过
hashCode()计算和equals()方法调用,查找/插入略快 - 不依赖用户代码正确性,适合框架底层或调试工具等高可靠性场景
- 尤其适合那些本身就不该重写
equals()的类(比如某些代理对象、动态生成对象)
支撑对象图遍历这类特殊需求
在序列化、深拷贝、对象图分析等场景中,必须严格记录“处理过哪个对象”,而不是“处理过内容为 X 的对象”。否则遇到循环引用或同值多实例时会出错。
- 比如反序列化时,A 引用 B,B 又引用 A —— 需靠 IdentityHashMap 记录“已见过 A 这个实例”,而非“见过某个字段等于 A 的对象”
- 调试器为每个运行时对象创建唯一代理,也必须按地址区分,不能按 toString() 或字段值
设计上主动放弃 Map 接口的部分约定
Java 官方明确说明:IdentityHashMap 是故意违反 Map 接口通用契约的。它不承诺遵循 equals()/hashCode() 语义,因此不能替代 HashMap 用于业务逻辑。
- 它的存在不是为了“更好用”,而是为了“不可替代”
- 适用范围极窄,但一旦需要,就没有其他标准 Map 能胜任
- 用错场景(比如拿它存用户 ID 字符串)会导致难以排查的 null 返回或数据丢失










