identityhashmap 的 key 比较用 == 而非 equals(),故 new string("a") 与 "a" 被视为不同 key;null key 处理也基于 ==;它不支持并发,synchronizedmap 包装无效;扩容时用 system.identityhashcode(),无视重写的 hashcode();设计初衷是避免 equals() 副作用,非通用映射。

IdentityHashMap 的 key 比较到底用的是 == 还是 equals()
它用的是 ==,不是 equals()。这意味着两个逻辑上相等的 String 对象(比如 new String("a") 和 "a"),只要不是同一个对象实例,就会被当成不同 key 存进去。
常见错误现象:往 IdentityHashMap 里反复 put 相同语义的 key,结果 size 持续增长,查不到值;或者用 get() 拿不到刚 put() 进去的东西——因为传入的 key 是新对象,内存地址不同。
- 只适合你**明确控制 key 实例生命周期**的场景,比如缓存 Class 对象、Enum 实例、或你自己 new 出来的固定单例
- 不能替代
HashMap做通用映射,尤其别用来存字符串字面量和用户输入 - 性能上比
HashMap略快一点(省了equals()调用),但代价是语义断裂,调试时容易懵
IdentityHashMap 和 HashMap 在 null key 处理上有什么区别
两者都允许 null 作为 key,但判断方式不同:IdentityHashMap 把 null 当作一个特殊地址值处理,用 == 判定是否为同一 null 引用;HashMap 则专门在代码里分支处理 null。
使用场景:当你需要把 null 当作一个合法、唯一的 key,并且不希望它和某个“逻辑上等于 null”的对象混淆时,IdentityHashMap 更严格。
立即学习“Java免费学习笔记(深入)”;
-
IdentityHashMap中,所有nullkey 都指向同一个桶,因为所有null引用的地址值在 JVM 中是统一的 - 但如果你误以为
new Object() == null或者Objects.equals(null, someObj)会影响行为,那就踩坑了——它根本不走equals() - 注意:
IdentityHashMap的containsKey(null)返回true当且仅当真有null被 put 过,不会被子类重写影响
为什么 IdentityHashMap 不支持并发,而且不能被 Collections.synchronizedMap 包装
它的哈希表实现没加锁,也没用 volatile 或 CAS 控制状态;更关键的是,Collections.synchronizedMap 包装后,只同步了 public 方法,但 IdentityHashMap 内部的 resize、rehash 等操作仍可能在多线程下看到不一致的数组状态。
错误现象:多线程 put 后 size 返回负数、get() 随机返回 null、甚至 ConcurrentModificationException(虽然它没用 modCount)。
- 不要在多个线程里共享未加锁的
IdentityHashMap实例 -
Collections.synchronizedMap(new IdentityHashMap())是伪安全——方法调用串行了,但内部结构仍可能被破坏 - 真要线程安全,要么用
ConcurrentHashMap(但它是基于equals()的),要么自己用synchronized块包裹整个操作序列
IdentityHashMap 的迭代顺序和扩容机制和 HashMap 一样吗
不一样。它不保证任何顺序,也不按哈希值排序;它的桶数组扩容策略类似 HashMap(2 的幂次),但 rehash 时直接用 System.identityHashCode() 计算新位置,不调用 hashCode() 方法。
这意味着:哪怕你重写了某个类的 hashCode(),IdentityHashMap 也完全无视它;它只认 JVM 分配的那个原始内存地址哈希值。
- 迭代顺序完全取决于对象创建时机和 GC 活动,每次运行都可能不同
- 如果 key 对象被移动过(比如经过一次 full GC),
System.identityHashCode()通常不变,但极端情况下可能变化——这会导致 map 行为异常,不过概率极低 - 别拿它做测试用的“稳定哈希容器”,它比
LinkedHashMap还不可靠
最常被忽略的一点:IdentityHashMap 的设计初衷不是为了“高性能哈希表”,而是为了在需要引用相等性(reference equality)的底层框架中避免 equals() 带来的副作用——比如在序列化、代理、注解处理器里临时缓存对象。一旦脱离这个上下文,它就很容易变成 bug 温床。








