为什么重写 equals 方法就必须重写 hashCode 方法?

简介: 为什么重写 equals 方法就必须重写 hashCode 方法?

简答版

       因为我们在使用 HashMap 或 HashSet 集合类的时候,需要用到哈希表,哈希表必须满足 两个对象 equals 返回 true时,两个对象 hashCode 返回的哈希值必须相同,而我们重写equals方法后,可能导致两个对象 equals 返回 true ,而 hashCode 返回的哈希值不相同,导致哈希表中存储了两个相同的对象

详答版

我们知道

  • 当两个对象 equals 返回 true 时,则两个对象就是相同的
  • 哈希表中不能存储两个相同的元素

哈希表 的原理是

  • 先比较两个对象的哈希值,如果哈希值不同,则这两个对象不可能相同,无需调用 equals 方法进行比较
  • 如果哈希值相同,这两个对象不一定相等,因此会再使用 equals 方法进行比较,来确定这两个对象是否相等
  • 并且哈希表中不能存储两个相同的元素

因此 equals 方法 和 hashCode方法必须满足

  • hashCode相同时,equals 方法不一定返回 true
  • equals 方法返回 true 时,两个对象 hashCode 返回的哈希值必须相同

而如果重写了 equals 方法,而没有重写 hashCode 方法,就有可能导致 equals 返回true,而hashCode 返回的哈希值不相同

那么哈希表在存储数据的时候,比较到两个对象的哈希值不相同,就认为两个对象不同,不在调用equals方法,将两个对象都存储在哈希表中,这就导致了哈希表的错误

我们看下面的一个例子

import java.util.Objects;
public class Student {
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
}

上面的Student类我们重写了equals方法,但没有重写hashCode方法,然后进行测试

public class Test {
    public static void main(String[] args) {
        Student student01=new Student("张三",18);
        Student student02=new Student("张三",18);
        System.out.println(student01.equals(student02));
        Set<Student> studentSet = new HashSet<>();
        studentSet.add(student01);
        studentSet.add(student02);
        System.out.println(studentSet);
    }
}

8bdc2a4587e54eae870598d02aebfb83.png

我们看到 HashSet 是 不可重复 的集合,却存入了两个相同的对象

现在我们重写hashCode方法

import java.util.Objects;
public class Student {
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

在进行相同的测试

我们看到了这次就存储进一个对象

结论

重写equals方法的时候一定要重写hashCode方法

我们在自定义类的时候,equals方法默认使用 == 比较对象地址,而我们一般会重写以比较对象属性,而在idea中,当我们重写equals方法的时候,会自动帮我们重写hashCode方法

目录
相关文章
|
存储 Java
【面试题精讲】为什么重写equals时必须重写hashCode方法?
【面试题精讲】为什么重写equals时必须重写hashCode方法?
|
5月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
|
4月前
|
数据可视化 Java 开发工具
在分支回滚后,如何恢复被回滚的代码?
在分支回滚后,如何恢复被回滚的代码?
627 8
|
12月前
|
存储 边缘计算 开发工具
云计算技术:从基础到实践
【10月更文挑战第4天】云计算技术:从基础到实践
|
存储 运维 Oracle
国产数据库:目前最火的五款国产数据介绍
随着互联网的高速发展,目前数据的存储越来越多,传统的数据库逐渐不能满足人们对海量数据、高效查询的需求,国产的数据库如雨后春笋一样,一个个冒了出来来解决我们高速科技发展的数据库瓶颈,今天就给大家聊一聊目前最火的五款国产数据库,大家一起来交流一下。
国产数据库:目前最火的五款国产数据介绍
|
编译器 开发工具 C语言
配置C++的学习环境
这篇教程介绍了学习C++语言所需的环境配置和软件选择。首先,你需要一个文本编辑器(如Visual Studio Code、Visual Studio、Vim、Emacs或Eclipse)和一个C++编译器(如GCC)。在不同操作系统上安装GCC的方法包括:在Linux或UNIX上使用命令行检查或安装GCC,在Mac OS X上通过Apple的Xcode,而在Windows上则需要安装MinGW。教程还提供了使用Visual Studio创建和编译C++程序的步骤。最后,文章简述了g++编译器的使用及其常用命令选项。
274 0
|
7月前
|
人工智能 前端开发 Java
DDD四层架构和MVC三层架构的个人理解和学习笔记
领域驱动设计(DDD)是一种以业务为核心的设计方法,与传统MVC架构不同,DDD将业务逻辑拆分为应用层和领域层,更关注业务领域而非数据库设计。其四层架构包括:Interface(接口层)、Application(应用层)、Domain(领域层)和Infrastructure(基础层)。各层职责分明,避免跨层调用,确保业务逻辑清晰。代码实现中,通过DTO、Entity、DO等对象的转换,结合ProtoBuf协议,完成请求与响应的处理流程。为提高复用性,实际项目中可增加Common层存放公共依赖。DDD强调从业务出发设计软件,适应复杂业务场景,是微服务架构的重要设计思想。
|
JavaScript 前端开发
【Web 前端】jQuery 库中的 $() 是什么?
【5月更文挑战第1天】【Web 前端】jQuery 库中的 $() 是什么?
|
12月前
|
消息中间件 负载均衡 算法
聊聊 RocketMQ中 Topic,Queue,Consumer,Consumer Group的关系
本文详细解析了RocketMQ中Topic、Queue、Consumer及Consumer Group之间的关系。文中通过图表展示了Topic可包含多个Queue,Queue分布在不同Broker上;Consumer组内多个消费者共享消息;并深入探讨了集群消费与广播消费模式下Queue与Consumer的关系,以及Rebalancing机制在实例增减时如何确保负载均衡。理解这些关系有助于更好地掌握RocketMQ的工作原理,提升系统运维效率。
2541 2
|
Python
Python pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
Python pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
1083 0