identityhashmap 在对象序列化中用于精准识别同一对象实例以拦截循环引用,因其基于 == 比较和 system.identityhashcode(),避免 equals()/hashcode() 重写导致的误判,被 gson、jackson 等框架用作引用注册表。

IdentityHashMap 在对象序列化中主要用于精准识别“同一个对象实例”,从而可靠检测和拦截循环引用。它不看内容是否相同,只认内存地址是不是同一个——这正是处理对象图时避免无限递归的关键。
为什么必须用 IdentityHashMap 而不是 HashMap?
普通 HashMap 依赖 equals() 和 hashCode(),两个内容相同的对象(比如两个 new String("a"))会被当作同一键,导致误判已访问;而 IdentityHashMap 使用 == 比较,只要不是同一个实例,就视为新对象。
- 对象图中常存在多个“值相同但实例不同”的节点,比如多个空字符串、重复配置对象
- 代理类、AOP 增强对象、内部匿名类等,
equals()可能被重写或不可靠 - 序列化框架(如 Gson、Jackson 内部机制)实际就用 IdentityHashMap 做引用注册表
典型序列化场景中的使用模式
在自定义 writeObject 或序列化遍历逻辑中,用 IdentityHashMap 记录“已见过的对象引用”:
- 首次遇到某对象:存入 map,标记为已访问,并继续递归序列化其字段
- 再次遇到同一对象(即
visited.containsKey(obj)返回 true):不重复展开,改写一个引用标记(如{"$ref": "id123"}) - key 用对象本身(
obj),value 可存序号、路径或占位符信息
配合 System.identityHashCode 的稳定性
IdentityHashMap 底层调用 System.identityHashCode() 计算哈希值,该值基于对象内存地址生成,即使对象重写了 hashCode() 也不受影响,保证了:
- 对象在生命周期内哈希值不变(除非被 GC 移动,但 JVM 中 identity hash 不随移动改变)
- 不同实例即使
equals()返回 true,哈希也大概率不同,减少冲突 - 与 == 判断严格对齐,语义一致、行为可预测
实际代码片段示意
一个简化但可用的遍历保护逻辑:
IdentityHashMap<object integer> visited = new IdentityHashMap();
int nextId = 0;
void serialize(Object obj) {
if (obj == null) return;
if (visited.containsKey(obj)) {
writeRef(visited.get(obj)); // 写入已有 ID
return;
}
visited.put(obj, nextId);
writeStartObject();
writeId(nextId++);
writeFields(obj);
writeEndObject();
}</object>
注意:真实序列化框架还会处理 null 键、线程安全、泛型擦除等细节,但核心引用跟踪逻辑完全依赖 IdentityHashMap 的引用语义。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











