# 问题

3. 为什么 HashMap 需要重写 hashCode()equals() 方法?

# 标准答案

在 HashMap 中,hashCode()equals() 方法用于确定 key 在数组中的存储位置,并在哈希冲突时正确匹配 key

  • hashCode() 决定 key 的哈希值,并通过 (n - 1) & hash 计算存储位置。
  • equals() 用于比较 key 是否相等,保证 HashMap 在哈希冲突时能正确匹配 key,而不会误判不同 key。

如果 hashCode()equals() 没有正确重写,可能导致 相同对象存储到不同位置、不同对象被误认为相同、get() 失败、数据丢失等问题

# 答案解析

# 1. HashMap 的存取流程

HashMap 通过 哈希函数 + 数组 + 链表(或红黑树) 存储键值对,存取过程如下:

  1. 计算 key 的 hashCode(),用 (h = key.hashCode()) ^ (h >>> 16) 计算哈希值,减少冲突。
  2. 计算 key 的存储索引,用 (n - 1) & hash 确定数组索引。
  3. 插入 key-value
    • 如果桶为空,直接插入。
    • 如果桶不为空,则遍历链表/红黑树:
      • equals() 判断 key 是否已存在,相等则更新 value。
      • 否则插入到链表尾部或转换为红黑树。
  4. 查找 key 时:
    • 先通过 hashCode() 定位桶。
    • 再通过 equals() 遍历该桶的链表/红黑树查找 key。

# 2. 为什么 hashCode() 需要重写?

  • hashCode() 直接影响 key 的 存储位置,如果不同对象的 hashCode() 相同,容易导致 哈希冲突,影响 HashMap 性能。
  • 默认的 Object.hashCode() 可能不适用于自定义对象。例如:
    class User {
        String name;
    }
    
    1
    2
    3
    由于 User 没有重写 hashCode(),它默认使用 Object.hashCode(),不同 User 对象即使 name 相同,也会有不同的 hashCode(),导致 HashMap 认为它们是不同 key。

正确写法

@Override
public int hashCode() {
    return Objects.hash(name);
}
1
2
3
4

错误写法

@Override
public int hashCode() {
    return 1;  // 导致所有 key 产生相同哈希值,退化为链表,查询性能降至 O(n)
}
1
2
3
4

# 3. 为什么 equals() 需要重写?

  • equals() 用于 哈希冲突后,确定 key 是否相等,确保 get() 时能正确找到 key。
  • 如果 equals() 没有正确实现,可能导致 相同 key 被认为不同,导致 put() 重复插入,get() 失败

正确写法

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    User user = (User) obj;
    return Objects.equals(name, user.name);
}
1
2
3
4
5
6
7

错误写法(忽略 null 或类型转换错误):

@Override
public boolean equals(Object obj) {
    return this == obj;  // 只判断内存地址,不符合业务需求
}
1
2
3
4

# 4. HashMap 的常见问题

❌ 常见误区

  1. 误认为 hashCode() 唯一就不需要 equals()

    • 错误示例
      @Override
      public int hashCode() {
          return id; // 仅使用 ID
      }
      
      1
      2
      3
      4
    • **问题:**如果 equals() 未重写,HashMap 仍然无法正确判断 key 是否相等,导致数据重复插入。
  2. 误认为 hashCode() 相同的对象一定相等

    • 由于哈希冲突,不同对象可能有相同的 hashCode(),但 equals() 仍需判断是否真的相等。
  3. 误认为 put() 方法会自动去重

    • put() 依赖 equals() 判断 key 是否已存在,否则可能导致重复 key 仍然插入。

# 深入追问

🔹 为什么 hashCode() 不能直接返回对象地址?
🔹 为什么 HashMap 不能只使用 hashCode() 进行 key 查找?
🔹 hashCode()equals() 如何优化 HashMap 的查询性能?
🔹 如果 hashCode() 计算过于复杂,会对 HashMap 造成什么影响?

# 相关面试题

hashCode()equals() 的默认实现是什么?
hashCode()equals() 违反一致性时会导致什么问题?
• HashMap 在高并发下如何保证数据一致性?
TreeMap 为什么只需要 compareTo() 而不需要 hashCode()