# 问题

11. IdentityHashMap 与 HashMap 的核心区别是什么?如何避免错误使用?

# 标准答案

IdentityHashMap 与 HashMap 的核心区别在于 key 的比较方式不同

  1. HashMap 使用 equals() 比较 key,确保逻辑上相等的 key 视为同一个;
  2. 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 的工作机制

  1. 存储结构:使用 Object 数组,key-value 交替存储:
    Object[] table = {key1, value1, key2, value2, ...};
    
    1
  2. put() 过程
    • 计算 System.identityHashCode(key) 作为 hash 值;
    • 通过线性探测法查找空位存入 table[];
    • 不调用 key.hashCode(),也不会使用 equals() 比较 key
  3. 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 存储

  1. 对象代理管理:如 Spring AOP 代理对象映射,避免 equals() 误判相等对象;
  2. JVM 运行时环境:如 ThreadLocal 内部使用 IdentityHashMap 维护线程变量;
  3. 编译器优化:存储 AST 解析过程中的唯一对象,避免 equals() 影响语法树。

# 深入追问

🔹 为什么 IdentityHashMap 采用 System.identityHashCode() 计算 hash?
🔹 为什么 IdentityHashMap 不使用链地址法,而是用线性探测?
🔹 IdentityHashMap 和 WeakHashMap 能否结合使用?

# 相关面试题

IdentityHashMap 的 == 比较如何影响 GC?
IdentityHashMap 在高并发环境下是否安全?如何优化?
IdentityHashMap 适用于哪些 JVM 内部场景?
Java 还有哪些特殊用途的 Map 实现?(如 EnumMap、WeakHashMap)