一文让你搞懂:为什么重写equals方法的时候要同时重写hashCode方法

😀大家好!我是向阳🌞,一个想成为优秀全栈开发工程师的有志青年!	
📔今天来说一说为什么重写equals方法的时候要同时重写hashcode方法。

hashCode

hashCode即散列码,hashCode方法返回对象的哈希码,返回值是int类型,是一个整数。hashCode主要用于一些支持哈希表数据结构的集合,如HashMap、HashSet等,用来快速确定对象的存储位置,好的hashCode方法应该生成均匀的散列码。

默认会根据对象的内存地址生成哈希码,如下图所示。

在这里插入图片描述

重写hashCode方法的基本规则

  • 如果两个对象的equals方法判断相等,则他们的hashCode方法一定相等
  • 如果两个对象的equals方法不相等,那他们的hashCode方法不必不相等
  • 同一个对象多次调用 hashCode 方法,必须返回相同的值。

equals

equals方法用于判断两个对象是否相等,默认实现是使用 == 来比较两个对象的内存地址是否相等,如下图所示。

在这里插入图片描述

我们可以在类中通过重写equals方法来定义自己的判断是否相等逻辑,这也是为了现实生活中一些场景的合理性。

重写equals方法的基本规则

五大规则:

  • 自反性:对于任何非空对象引用 x,x.equals(x) 必须为true
  • 对称性:对于任何非空对象引用 x 和 y,x.equals(y) 应当等于 y.equals(x)
  • 传递性:如果 x.equals(y) == truey.equals(z) == ture,那么x.equals(z) == ture
  • 一致性:只要对象未发生改变,多此调用 x.equals(y) 结果应该一致。
  • 对于null:对于任何非空对象引用 x,x.equals(null),必须返回false。

两者之间的关系

  1. 如果两个对象根据equals方法判断相等,则他们的hashCode值一定相等,即 a.equals(b)==true,则a.hashCode()==b.hashCode()。

  2. 如果两个对象的hashCode值相等,但是他们的equals不一定相等。

我们一定要遵循上述两个规则,否则在基于哈希表的集合中会出现错误,HashMap、HashSet这些集合内部依赖对象的 hashCode 方法和 equals 方法来确定元素的存储位置,如果没有正确重写这两个方法,则集合无法正确判断对象的相等性。

只重写equals方法

我们来试一试只重写equals方法会出现什么问题呢?我们先来写一个Person类,只重写equals方法。

Person类:

注意:String类的equals类内部已经经过重写了,比较的是两个字符串的值是否相等,而不是比较的地址值。

public class Person {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

我们使用HashSet来进行测试,HashSet的特性就是去重,向HashSet插入两个属性值一模一样的Person类,来看看会插入一个还是两个呢。

测试类:

public class Test {

    public static void main(String[] args) {
        HashSet<Person> hashSet = new HashSet<>();

        Person person1 = new Person();
        person1.setName("张三");
        person1.setAge(18);

        Person person2 = new Person();
        person2.setName("张三");
        person2.setAge(18);

        System.out.println(person1.equals(person2));

        hashSet.add(person1);
        hashSet.add(person2);

        for (Person person : hashSet) {
            System.out.println(person.toString());
        }
    }
}

打印结果:

在这里插入图片描述
从上述的打印结果可以看出来,已经出现了严重的问题,我们插入两个一模一样的对象属性,却都插入进去,没有去重。

只重写hashCode方法

把上述的hashCode方法改成下面这个,注释掉equals方法。

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

测试类代码不变,我们可以看到,结果还是如此,HashSet插入了两个一模一样的元素。

在这里插入图片描述

源码分析出现上述的问题

我们追踪到HashSet的 add 方法中,由于我们上面的Person类没有重写hashCode方法,我们这里计算hash值就是调用的Object的hashCode方法

在这里插入图片描述
接着我们进入到核心方法 putVal 中,我们可以看到,判断条件都是先判断hash值是否相等,如果相等才进行判断equals方法是否相等。

所以我们在判断的时候,hash值不相等,直接返回的就是false,不会进行判断equals方法是否相等,所以添加第二个Person实例对象的时候,由于hash值不相等,直接插入到HashSet当中了。

如果我们没有重写equals方法,在判断key.equals(k)的时候,调用的是Object的equals方法,两个对象的内存地址不相同,判断的返回结果肯定是false,在相同key下值不会被覆盖,而是添加了进来。

在这里插入图片描述

两个方法都进行重写

Person类完整代码:

public class Person {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

这个时候我们运行测试类,可以发现HashSet中只有一个值。

在这里插入图片描述

总结

hashCode 和 equals 两个方法是用来协同判断两个对象是否相等的,如果在重写 equals方法 时,不重写 hashCode方法,或者只重写 equals 方法,不重写 hashCode 方法,就会导致在基于哈希表的数据结构进行数据操作时出现一些问题,例如将两个相等的自定义对象存储在 Set 集合时,就会出现异常,所以我们在重写 equals 方法的同时也要重写 hashCode 方法
请添加图片描述

——👦[作者]:向阳256
——⏳[更新]:2025.5.14
——🥰本人技术有限,如果有不对指正需要更改或者有更好的方法,欢迎到评论区留言。如果文章对您有帮助,请观众老爷您赏个赞吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值