# 问题
11. IdentityHashMap 与 HashMap 的核心区别是什么?如何避免错误使用?
# 标准答案
IdentityHashMap 与 HashMap 的核心区别在于 key 的比较方式不同:
- HashMap 使用
equals()
比较 key,确保逻辑上相等的 key 视为同一个; - IdentityHashMap 使用
==
比较 key,只有同一个对象引用才视为相同 key,即便equals()
认为相等,也不会合并。
由于 IdentityHashMap 依赖 对象引用相等性,它通常用于 需要区分同值不同实例的场景,如 JVM 解析、代理对象缓存等。但误用可能导致Key 重复问题,在大多数业务代码中,不建议用它替代 HashMap。
# 答案解析
# 1. IdentityHashMap 的底层实现
IdentityHashMap 并没有使用 数组 + 链地址法,而是直接使用一个 Entry 数组:
transient Object[] table; // 存储 key-value 交替出现
1
- table 结构:所有 Key 和 Value 存在同一个 Object 数组,
table[2i]
存 key,table[2i+1]
存 value。 - 查找 key 时使用
==
直接比较引用,避免equals()
方法开销。
# 2. IdentityHashMap 与 HashMap 的核心区别
对比项 | HashMap | IdentityHashMap |
---|---|---|
Key 比较方式 | equals() 判断逻辑相等 | == 判断是否是同一对象 |
数据结构 | 数组 + 链表/红黑树 | Object 数组 |
哈希计算 | 调用 key.hashCode() | 使用 System.identityHashCode(key) |
适用场景 | 绝大多数业务场景 | 代理对象、编译器、对象唯一性判断 |
性能 | 较稳定,适用于通用用途 | 避免 equals() 调用,适用于特定场景 |
线程安全 | 不是线程安全的 | 不是线程安全的 |
# 3. IdentityHashMap 的工作机制
- 存储结构:使用 Object 数组,key-value 交替存储:
Object[] table = {key1, value1, key2, value2, ...};
1 - put() 过程
- 计算
System.identityHashCode(key)
作为 hash 值; - 通过线性探测法查找空位存入 table[];
- 不调用 key.hashCode(),也不会使用 equals() 比较 key。
- 计算
- get() 过程
- 计算
System.identityHashCode(key)
找到索引; - 通过
==
遍历查找相等的 key,并返回对应 value。
- 计算
# 4. 如何避免错误使用?
❌ 误区 1:误以为 IdentityHashMap 等价于 HashMap
- 错误示例:
Map<String, String> map = new IdentityHashMap<>(); map.put(new String("key"), "A"); map.put(new String("key"), "B"); System.out.println(map.size()); // 2,而不是 1!
1
2
3
4 - 问题解析:即使 key 的内容相同(
"key"
),但它们是不同对象(new String()
),==
认为是不同 key,导致重复存储。 - 正确方案:如果希望 key 逻辑相等的对象被视为同一个 key,应使用 HashMap。
❌ 误区 2:误以为 IdentityHashMap 可以用于缓存
- 错误示例:
Map<Integer, String> cache = new IdentityHashMap<>(); Integer a = 1000; Integer b = 1000; cache.put(a, "data1"); System.out.println(cache.get(b)); // null
1
2
3
4
5 - 问题解析:
Integer
在-128~127
之间是缓存对象,而 1000 以上是新创建的对象,因此a != b
,无法查找到 key。 - 正确方案:缓存应使用 HashMap 或
WeakHashMap
,避免 IdentityHashMap。
❌ 误区 3:误以为 IdentityHashMap 适用于所有高性能场景
- 错误示例:
IdentityHashMap<Integer, String> map = new IdentityHashMap<>(); for (int i = 0; i < 1000000; i++) { map.put(i, "value"); }
1
2
3
4 - 问题解析:IdentityHashMap 使用线性探测法存储 key-value,数据量大时容易冲突,影响性能。
- 正确方案:大数据量时,仍应使用 HashMap,甚至
ConcurrentHashMap
。
# 5. 适用场景
IdentityHashMap 适用于对象唯一性判断或不需要逻辑相等性的 key 存储:
- 对象代理管理:如 Spring AOP 代理对象映射,避免
equals()
误判相等对象; - JVM 运行时环境:如
ThreadLocal
内部使用IdentityHashMap
维护线程变量; - 编译器优化:存储 AST 解析过程中的唯一对象,避免
equals()
影响语法树。
# 深入追问
🔹 为什么 IdentityHashMap 采用 System.identityHashCode()
计算 hash?
🔹 为什么 IdentityHashMap 不使用链地址法,而是用线性探测?
🔹 IdentityHashMap 和 WeakHashMap 能否结合使用?
# 相关面试题
• IdentityHashMap 的 ==
比较如何影响 GC?
• IdentityHashMap 在高并发环境下是否安全?如何优化?
• IdentityHashMap 适用于哪些 JVM 内部场景?
• Java 还有哪些特殊用途的 Map 实现?(如 EnumMap、WeakHashMap)