IdentityHashMap用==比较对象引用而非equals()和hashCode(),故以内存地址判等;适用于需区分值相等但实例不同的场景,如代理对象缓存、对象快照映射等,但字符串或Integer等有常量池的类型易误用,且性能与线程安全受限。

IdentityHashMap 为什么能用内存地址判断 key 唯一性
因为 IdentityHashMap 不调用 equals() 和 hashCode(),而是直接用 == 比较对象引用。只要两个 key 是同一个对象(即堆中同一地址),就视为相同;哪怕内容完全一样但不是同一个实例,也会被当作不同 key。
这和 HashMap 的行为有本质区别:后者依赖 equals() 判等,容易受重写影响;而 IdentityHashMap 绕过所有逻辑,只认“是不是同一个东西”。
什么时候必须用 IdentityHashMap 而不是 HashMap
典型场景是 key 本身重写了 equals() 或 hashCode(),但你又需要区分“值相等但实例不同”的情况。比如:
- 缓存动态生成的代理对象(如 CGLIB 生成的类,多个实例内容一致但
==为 false) - 追踪对象生命周期,避免因
equals()被误合并(如调试器中的对象快照映射) - 实现自定义的引用级去重逻辑,且不希望受业务 equals 规则干扰
如果只是想存字符串或 Integer,别用 IdentityHashMap——它们的字面量常量池或缓存机制会让 == 表现不可控,反而引发意外覆盖。
常见误用:把 new String("a") 当作唯一 key
这段代码很容易让人误解:
IdentityHashMap<string integer> map = new IdentityHashMap();
map.put(new String("a"), 1);
map.put(new String("a"), 2); // 这个不会覆盖上一个!size 变成 2
</string>
因为两个 new String("a") 是不同对象,== 为 false,所以都被保留。但如果你写的是:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
String a = "a"; map.put(a, 1); map.put(a, 2); // 这里会覆盖,因为是同一个字符串常量引用
注意:字符串字面量、Integer(-128~127) 等有缓存,用 == 判断时行为和预期可能相反。
性能与线程安全的隐含代价
IdentityHashMap 内部用的是线性探测法的哈希表,没有红黑树优化,大量冲突时性能退化明显;而且它不是线程安全的,连 Collections.synchronizedMap() 包裹后也不能保证原子性——因为它的判等逻辑绕过了同步边界。
如果需要并发场景下的引用级映射,得考虑 ConcurrentHashMap + 自定义 key 封装(比如把原始引用转成 System.identityHashCode() 加对象弱引用),或者直接用 WeakReference 配合标识符管理。
真正要用 IdentityHashMap,往往是调试、框架底层或极少数需绕过 equals 的场合。日常业务逻辑里硬套它,八成是在掩盖设计问题。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










