侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

如何正确重写equals()和hashCode()方法?

2025-12-13 / 0 评论 / 4 阅读

题目

如何正确重写equals()和hashCode()方法?

信息

  • 类型:问答
  • 难度:⭐⭐

考点

对象相等性,集合框架,方法重写规范

快速回答

正确重写equals()和hashCode()需要遵循以下原则:

  • equals()规范:满足自反性、对称性、传递性、一致性和非空性
  • hashCode()规范:相等对象必须有相同哈希码,不相等对象尽量不同
  • 同步重写:重写equals()时必须同时重写hashCode()
  • 使用关键字段:计算应基于equals()比较中使用的字段
## 解析

1. 原理说明

在Java中,equals()定义对象逻辑相等性,hashCode()为对象生成哈希值用于散列存储。两者必须协同工作:

  • 当两个对象equals()返回true时,它们的hashCode()必须相同
  • 哈希冲突时(不同对象相同哈希码),集合会使用equals()进一步验证
  • 违反约定会导致HashMapHashSet等集合行为异常

2. 代码示例

public class Person {
    private String name;
    private int age;

    // 构造方法省略

    @Override
    public boolean equals(Object o) {
        // 1. 地址相同
        if (this == o) return true;
        // 2. 非空且类型相同
        if (o == null || getClass() != o.getClass()) return false;
        // 3. 字段比较
        Person person = (Person) o;
        return age == person.age && 
               Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        // 使用相同字段计算哈希码
        return Objects.hash(name, age);
    }
}

3. 最佳实践

  • 使用Objects.equals()比较字段避免NPE
  • 优先使用Objects.hash()计算哈希码
  • 不可变对象可缓存哈希码值
  • 在子类中通过super.equals()包含父类字段

4. 常见错误

  • 错误1:只重写equals()不重写hashCode()
    // 导致相同对象在HashMap中存储两次
    Map<Person, String> map = new HashMap<>();
    Person p1 = new Person("Alice", 30);
    Person p2 = new Person("Alice", 30);
    map.put(p1, "value1");
    map.put(p2, "value2"); // 预期覆盖但实际新增条目
  • 错误2:使用可变字段参与计算
    // 对象放入HashSet后修改字段
    Set<Person> set = new HashSet<>();
    Person p = new Person("Bob", 25);
    set.add(p);
    p.setAge(30);  // 修改后hashCode变化,导致无法检索
  • 错误3:忽略null检查
    // 未检查参数类型直接转型
    public boolean equals(Object o) {
        Person other = (Person) o; // 可能抛出ClassCastException
        // ...
    }

5. 扩展知识

  • Lombok简化:使用@EqualsAndHashCode注解自动生成
  • 性能优化:对计算代价高的哈希码进行缓存
    private int hash; // 默认0
    
    @Override
    public int hashCode() {
        int h = hash;
        if (h == 0) {
            h = Objects.hash(name, age);
            hash = h;
        }
        return h;
    }
  • 继承处理:子类重写时需包含父类字段
    @Override
    public boolean equals(Object o) {
        if (!super.equals(o)) return false;
        // 比较子类特有字段...
    }
  • 集合影响HashMap通过(hash & (n-1))确定桶位置,哈希冲突会降低性能