已经实现equals重写,为什么还要实现hashcode重写?

  核心原因是:两个相等的对象必须具有相同的哈希码(hashCode),否则会导致哈希表等基于哈希的集合类出现逻辑错误。

  简单说,重写equals时必须重写hashCode,是为了保证 "逻辑相等的对象,在哈希表中也被当成同一个对象"。

打个比方:
equals就像 "身份证",判断两个对象是不是同一个人;
hashCode就像 "住址",告诉哈希表(如HashSet)这个对象该存在哪里。

如果两个人身份证(equals返回 true),但住址不同(hashCode不同),哈希表就会把他们当成两个人,分开存放 —— 这显然是错的。

所以,只要重写equals(定义了 "什么是同一个对象"),就得重写hashCode(保证同一个对象的住址相同),否则哈希表会出乱子。

以下是详细描述:

1. 官方规范要求

Java 官方文档明确规定了equals()hashCode()的关系:

  • 如果两个对象通过equals()比较返回true,则它们的hashCode()必须返回相同的整数
  • 反之,若两个对象的hashCode()不同,则equals()必须返回false(但hashCode()相同,equals()可能不同,即哈希冲突)。

如果违反这一规范,使用哈希表(如HashMapHashSet)时会出现不可预期的行为。

2. 哈希表的工作原理

哈希表(如HashMap)的存储和查找依赖以下步骤:

  1. 计算对象的hashCode(),确定其在哈希表中的桶(Bucket)位置
  2. 在对应桶中,通过equals()比较对象是否存在。

若只重写equals()而不重写hashCode(),会导致:

  • 两个equals()返回true的对象,可能被分配到不同的桶中。
  • 哈希表会错误地认为它们是不同的对象,从而出现重复存储或查找失败。

3. 反例:只重写equals()的问题

public class Person {
    private String id;

    public Person(String id) {
        this.id = id;
    }

    // 只重写equals(),未重写hashCode()
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return id.equals(person.id);
    }

    public static void main(String[] args) {
        Person p1 = new Person("123");
        Person p2 = new Person("123");

        System.out.println(p1.equals(p2)); // true(逻辑上相等)

        HashSet<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println(set.size()); // 输出2(错误!应视为同一个对象)
    }
}

问题分析
p1p2equals()返回true,但由于未重写hashCode(),默认使用Object类的hashCode()(基于内存地址),导致两者哈希码不同。HashSet会将它们存储在不同桶中,误认为是两个不同的对象。

4. 正确实现:同时重写equals()hashCode()

public class Person {
    private String id;

    public Person(String id) {
        this.id = id;
    }

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

    // 重写hashCode(),与equals()保持一致
    @Override
    public int hashCode() {
        return id.hashCode(); // 基于equals()中比较的字段计算哈希码
    }

    public static void main(String[] args) {
        Person p1 = new Person("123");
        Person p2 = new Person("123");

        System.out.println(p1.equals(p2)); // true
        System.out.println(p1.hashCode() == p2.hashCode()); // true

        HashSet<Person> set = new HashSet<>();
        set.add(p1);
        set.add(p2);

        System.out.println(set.size()); // 输出1(正确)
    }
}

修复原理
hashCode()基于equals()中用于比较的id字段计算,确保equals()返回true的对象一定有相同的哈希码,从而在哈希表中被正确识别为同一个对象。

5. 重写hashCode()的原则

  • 一致性:同一对象多次调用hashCode()应返回相同值(前提是对象未被修改)。
  • 相等性equals()返回true的两个对象,hashCode()必须相等。
  • 分散性equals()返回false的对象,应尽量让hashCode()不同(减少哈希冲突,提高哈希表效率)。

6. 实现技巧

  • 基于equals()中使用的所有字段计算哈希码。
  • 可通过Objects.hash()简化实现(自动处理null):
    @Override
    public int hashCode() {
        return Objects.hash(id, name, age); // 传入所有参与equals比较的字段
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值