Skip to content

重写 equals 方法时,建议把 hashcode 方法也重写,原因

约 830 字大约 3 分钟

Java基础美团

2025-03-26

⭐ 题目日期:

美团 - 2024/12/23

📝 题解:

在Java中,重写equals方法时必须同时重写hashCode方法,否则会违反对象契约,导致哈希集合类(如HashMapHashSet)的行为异常。以下是具体原因和实现建议:


一、核心原因

1. 哈希集合的依赖规则

  • Java对象契约规定
    • 若两个对象通过equals()比较相等(a.equals(b) == true),则它们的hashCode()必须返回相同的值。
    • 若两个对象hashCode()相同,equals()不一定为true(允许哈希冲突)。
  • 违反后果
    • 若仅重写equals()而不重写hashCode(),可能导致相等的对象拥有不同的哈希值。
    • 哈希集合(如HashMap)在存储或查找时,会优先依赖hashCode()定位桶(Bucket),若哈希值不同,即使对象相等也会被误判为不同,导致数据重复或丢失。

2. 示例场景

class Person {
    String name;
    int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    // 未重写hashCode()!
}
  • 问题
    若将两个equals相等的Person对象存入HashSet,由于默认hashCode()基于内存地址生成,两个对象可能被分配到不同哈希桶,导致重复存储。

二、实现原则

1. 重写hashCode()的目标

  • 一致性:相等对象的哈希值必须相同。
  • 分散性:不等对象的哈希值尽量不同(减少哈希冲突)。

2. 实现方法

使用对象关键字段的哈希值组合生成最终哈希码,通常借助Objects.hash()或手动计算:

@Override
public int hashCode() {
    // 使用IDE自动生成(推荐)
    return Objects.hash(name, age);

    // 或手动实现(等效):
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + age;  // 31为质数,减少冲突
    return result;
}

三、验证与工具

1. 单元测试

使用JUnit验证equalshashCode的一致性:

@Test
public void testEqualsAndHashCode() {
    Person p1 = new Person("Alice", 30);
    Person p2 = new Person("Alice", 30);
    assertEquals(p1, p2);
    assertEquals(p1.hashCode(), p2.hashCode());
}

2. Lombok注解

通过@EqualsAndHashCode自动生成equals()hashCode()

@EqualsAndHashCode
class Person {
    String name;
    int age;
}

四、常见问题

1. 为什么选择质数(如31)?

  • 质数乘法能更均匀地分散哈希值,减少冲突。
  • 31性能优化:31 * i = (i << 5) - i,JVM可优化为位运算。

2. 是否所有字段都要参与哈希计算?

  • 仅需关键字段:参与equals()比较的字段必须参与hashCode()计算。
  • 排除冗余字段:若某些字段不影响对象相等性(如缓存状态),可忽略。

3. 哈希冲突如何处理?

  • 哈希冲突是正常现象,哈希表通过链表或红黑树解决冲突。
  • 优化目标是降低冲突概率,而非完全避免。

五、总结

操作必要性后果
仅重写equals❌ 违反对象契约哈希集合行为异常(重复元素、查找失败)
同时重写hashCode✅ 符合规范确保对象在哈希集合中正确存储和检索

最终建议

  • 始终成对重写:重写equals()时必须重写hashCode()
  • 工具生成:使用IDE或Lombok自动生成,避免手动错误。
  • 覆盖所有关键字段:确保哈希值与对象相等性逻辑一致。