IdentityHashMap基于引用相等(==)判断键,适用于需区分同一对象实例的场景;WeakHashMap的键为弱引用,GC后自动清理条目,适用于附属信息缓存。二者均不支持null键,非线程安全,且不替代常规HashMap。

Java 中 IdentityHashMap 和 WeakHashMap 都是为解决普通 HashMap 在特定场景下行为不满足需求而设计的特殊映射实现。它们不按“逻辑相等”判断键,而是分别基于引用相等(==)和弱引用语义来管理键值对,适用于缓存、代理、上下文绑定等需要精细控制对象生命周期或身份判定的场景。
IdentityHashMap:用 == 判断键是否相同
当两个对象逻辑上 equals() 为 true,但你仍希望把它们当作不同键处理时,IdentityHashMap 就派上用场了。它内部使用哈希表,但比较键时直接用 ==(即内存地址),而非 equals() 和 hashCode()。
- 适合用于需要区分“同一对象实例”的场景,比如编译器符号表、AST 节点属性绑定、调试工具中的对象快照映射
- 注意:它的
hashCode()实际调用的是System.identityHashCode(),所以即使重写了hashCode(),也不会影响其行为 - 不能替代
HashMap做常规业务映射;若键对象未重写equals()或hashCode(),普通HashMap可能表现异常,此时可考虑IdentityHashMap作为临时规避手段(但更推荐修复对象契约)
WeakHashMap:键是弱引用,GC 后自动清理条目
WeakHashMap 的键被包装成 WeakReference,只要该键对象没有其他强引用指向它,下次 GC 就可能被回收,对应的映射条目也会随之失效并被移除(实际在访问时惰性清理)。
- 典型用途是构建“附属信息缓存”,例如为某个对象动态附加元数据(如序列化配置、格式化策略),又不想阻止该对象被回收
- 键必须是对象类型(不能是基本类型或 null),且建议避免用字符串字面量作键——因为字符串常量池中的字符串永远不会被 GC,导致条目长期滞留
- 不保证实时清理:GC 后条目不会立即消失,下一次调用
get()、put()或size()等方法时才会触发过期条目扫描和清理
选择依据:看你要控制的是“身份”还是“生命周期”
如果问题核心在于“两个内容相同的对象是否应视为同一个键”,选 IdentityHashMap;如果问题是“这个键对象用完就该被回收,我不想手动删映射”,那 WeakHashMap 更合适。
立即学习“Java免费学习笔记(深入)”;
- 二者都不支持
null键(会抛NullPointerException),但允许null值 - 都不是线程安全的,多线程环境下需额外同步(如用
Collections.synchronizedMap()包装,或改用ConcurrentHashMap配合其他机制) - 不要期望它们提供更强的一致性保障:比如
WeakHashMap的迭代器可能遇到已被 GC 的键,返回null值;IdentityHashMap的遍历顺序也不保证与插入顺序一致
简单示例对比
假设有两个内容相同的字符串:
String a = new String("key");
String b = new String("key"); // 内容相同,但不同实例
// 普通 HashMap 认为 a 和 b 是同一个键
HashMap<String, Integer> map1 = new HashMap<>();
map1.put(a, 1);
map1.put(b, 2); // 覆盖 a 的值,最终 size() == 1
// IdentityHashMap 认为 a 和 b 是不同键
IdentityHashMap<String, Integer> map2 = new IdentityHashMap<>();
map2.put(a, 1);
map2.put(b, 2); // 不覆盖,size() == 2
// WeakHashMap 示例:键指向一个大对象,无其他引用后会被自动清理
WeakHashMap<BigObject, String> weakMap = new WeakHashMap<>();
BigObject obj = new BigObject();
weakMap.put(obj, "meta");
obj = null; // 断开强引用
System.gc(); // 触发 GC(仅示意)
// 下次 weakMap.size() 可能变为 0


















