【Java基础】为什么要重写hashCode()和equals()

  • 概述
  • 不重写equals()会带来什么问题
  • 不重写hashCode()会带来什么问题
  • 总结

概述

  • equals()和hashCode()是java.lang.Object类的两个重要的方法,在实际应用中的类通常都需要重写这两个方法,那么究竟为什么要重写这两个方法呢?

  • 首先我们先来看一看Object类中的这两个方法是怎么写的:

public native int hashcode();

public boolean equals(Object paramobject){ 
    return(this == paramobject);
}
  • 从源码看出,Object类默认的equals()方法比较的是两个对象的内存地址(对于两个对象而言,==比较的是两个对象的地址值,对于基本数据类型而言,==比较的是具体的数值);而hashcode()是本地方法(用C语言实现的方法),因为Java的内存是安全的,因此无法根据散列码得到对象的内存地址;所以在Object类中,hashcode()方法是根据对象的内存地址用哈希算法算出来的一个int值。

不重写equals()会带来什么问题

  • 因此如果在一个类中不重写equals()方法,那么平时我们在代码中使用特别频繁的equals()方法直接就是拿你的对象的地址值去做比较的,而我们本意一般都是希望比较对象中实际存储的内容;

首先我们来定义一个类:

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

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = 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 String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试代码:

public static void main(String[] args) {
        Person p1 = new Person("小马",12);
        Person p2 = new Person("小马",12);
        Person p3 = p2;

        System.out.println(p1.equals(p2));//false
        System.out.println(p2.equals(p3));//true
        System.out.println(p1.equals(p3));//false
    }
  • 由于在Person类中并没有重写equals(),因此直接使用Object类中的equals()方法,比较的是地址值;因为p1与p2都是new 了一个person对象,因此指向堆内存中不同的区域,地址值也不一样;而p2将存储的地址值给了p3,p3与p2就同时指向堆内存中的同一块空间;
  • 而我们在写代码的时候,本意一般都是希望比较的是Person对象中的具体值内容;如果我们在刚在的Person类中加入equals()方法;
@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);
    }

再次运行刚才的测试代码,得到结果:

System.out.println(p1.equals(p2));//true
System.out.println(p2.equals(p3));//true
System.out.println(p1.equals(p3));//true
  • 这个时候我发现由于p1,p2,p3指向的对象中存储的内容一致,因此equals()返回的结果也都是ture了;

不重写hashCode()会带来什么问题

  • 到目前为止,小伙伴们可能会想,好像也并没有hashcode()什么事情啊;实际上,刚才的比较我们并没有用到hash算法,而hash算法在我们的代码中无处不在,为啥?举个例子,常用的集合中,基本上底层都是使用了Hash算法的,比如今天我们用HashSet来举个例子,看看不重写hashCode()会带来什么问题;

首先我们修改一下我们的测试代码:

Person p1 = new Person("小马",12);
Person p2 = new Person("小马",12);
Person p3 = new Person("小红",21);

Set set = new HashSet();

set.add(p1);
set.add(p2);
set.add(p3);

//结果:[Person{name='小马', age=12}, Person{name='小红', age=21}, Person{name='小马', age=12}]
System.out.println(set);
  • HashSet集合是可以自动去重的,而去重原理跟Hash也有很大的关系;如果不重写hashCode()方法,我们看到在set中打印出了三条信息,而其中有2条是重复的,而我们希望它能根据内容进行去重,怎么做呢?
  • 我们在Person类中重写一下HashCode()方法:
@Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

再次运行测试代码,得到结果:

//结果:[Person{name='小红', age=21}, Person{name='小马', age=12}]
System.out.println(set);
  • 我们发现已经根据内容进行了去重;
  • 具体流程可以理解为,添加p2的时候,add()方法会调用"p2"的hashcode方法计算哈希值(元素在集合底层数组中存储的位置),若哈希值是123456,在集合中找有没有123456这个哈希值的元素,发现有(哈希冲突),"p2"调用equals()方法和哈希值相同的元素进行比较,p2.equals(p1)返回true,内容一致,不再存储p2;

因此重写hashCode()的重要性不言而喻,而从另一个角度来讲,根据hashCode的规则,两个对象相等其哈希值一定相等;然而重写了equals,且p1.equals(p2)返回true,因此p1与p2的哈希值结果也应该相等才对,而如果不重写,让hashCode()是根据地址值去计算的哈希值,那么p1与p2的哈希值就会不一致,就违背了hashcode的规则;

总结

如果在类中不重写equals()方法,那么类对象在调用equals()方法的时候比较的就是地址值,我们希望在后续的对象比较中比较内容而不是地址值,就需要重写equals()方法;

如果不重写hashCode()方法,那么调用hashCode()方法的时候就是根据地址值计算出来的哈希值,如果希望在使用到哈希值的时候(比如,Set集合元素去重)是根据内容计算的哈希值而不是根据地址值计算的哈希值,就需要重写hashCode()方法;

当然,重写equals()与hashCode()方法是可以通过IDE自动生成;

当然,根据hashcode的规则,如果两个对象equals()比较为true,那么hashCode也应该一致,因此建议我们不仅要重写equals(),同时还应该重写hashCode()方法。

注意:本文归作者所有,未经作者允许,不得转载

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maoway稻草人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值