为什么重写 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方法?
|
存储 运维 Oracle
国产数据库:目前最火的五款国产数据介绍
随着互联网的高速发展,目前数据的存储越来越多,传统的数据库逐渐不能满足人们对海量数据、高效查询的需求,国产的数据库如雨后春笋一样,一个个冒了出来来解决我们高速科技发展的数据库瓶颈,今天就给大家聊一聊目前最火的五款国产数据库,大家一起来交流一下。
国产数据库:目前最火的五款国产数据介绍
|
存储 边缘计算 开发工具
云计算技术:从基础到实践
【10月更文挑战第4天】云计算技术:从基础到实践
|
编译器 开发工具 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++编译器的使用及其常用命令选项。
422 0
|
消息中间件 负载均衡 算法
聊聊 RocketMQ中 Topic,Queue,Consumer,Consumer Group的关系
本文详细解析了RocketMQ中Topic、Queue、Consumer及Consumer Group之间的关系。文中通过图表展示了Topic可包含多个Queue,Queue分布在不同Broker上;Consumer组内多个消费者共享消息;并深入探讨了集群消费与广播消费模式下Queue与Consumer的关系,以及Rebalancing机制在实例增减时如何确保负载均衡。理解这些关系有助于更好地掌握RocketMQ的工作原理,提升系统运维效率。
3058 2
|
JavaScript 前端开发
【Web 前端】jQuery 库中的 $() 是什么?
【5月更文挑战第1天】【Web 前端】jQuery 库中的 $() 是什么?
|
Python
Python pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
Python pip : 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
1558 0
|
Linux
Linux系统盘制作(UltraISO)
Linux系统盘制作(UltraISO)
815 1
|
安全 API 开发者
智能体-Agent能力升级!新增Assistant API & Tools API服务接口
ModelScope-Agent是一个交互式创作空间,它支持LLM(Language Model)的扩展能力,例如工具调用(function calling)和知识检索(knowledge retrieval)。它已经对相关接口进行了开源,以提供更原子化的应用LLM能力。用户可以通过Modelscope-Agent上的不同代理(agent),结合自定义的LLM配置和消息,调用这些能力。
|
XML 监控 Java
如何在Spring Boot中使用AOP
如何在Spring Boot中使用AOP