为什么重写equals方法必须重写hashCode方法
创始人
2025-05-28 13:00:31

equals 方法和 hashCode 方法是 Object 类中的两个基础方法,它们共同协作来判断两个对象是否相等。为什么要这样设计嘞?原因就出在“性能” 2 字上。

使用过 HashMap 我们就知道,通过 hash 计算之后,我们就可以直接定位出某个值存储的位置了,那么试想一下,如果你现在要查询某个值是否在集合中?如果不通过 hash 方式直接定位元素(的存储位置),那么就只能按照集合的前后顺序,一个一个的询问比对了,而这种依次比对的效率明显低于 hash 定位的方式。这就是 hash 以及 hashCode 存在的价值。

当我们对比两个对象是否相等时,我们就可以先使用 hashCode 进行比较,如果比较的结果是 true,那么就可以使用 equals 再次确认两个对象是否相等,如果比较的结果是 true,那么这两个对象就是相等的,否则其他情况就认为两个对象不相等。这样就大大的提升了对象比较的效率,这也是为什么 Java 设计使用 hashCode 和 equals 协同的方式,来确认两个对象是否相等的原因。

那为什么不直接使用 hashCode 就确定两个对象是否相等呢?

这是因为不同对象的 hashCode 可能相同;但 hashCode 不同的对象一定不相等,所以使用 hashCode 可以起到快速初次判断对象是否相等的作用。

但即使知道了以上基础知识,依然解决不了本篇的问题,也就是:重写 equals 时为什么一定要重写 hashCode?要想了解这个问题的根本原因,我们还得先从这两个方法开始说起。

 1.equals 方法

Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。在 Object 类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。

equals 方法的实现源码如下:

public boolean equals(Object obj) {  return (this == obj);  
} 

通过上述源码和 equals 的定义我们可以看出,在大多数情况来说,equals 的判断是没有什么意义的!例如,使用 Object 中的 equals 比较两个自定义的对象是否相等,这就完全没有意义(因为无论对象是否相等,结果都是 false)

2.hashCode 方法

相等的值 hashCode 一定相同,不同的值 hashCode 也有可能相同

3.为什么要一起重写?

3.1 Set 正常使用

Set 集合是用来保存不同对象的,相同的对象就会被 Set 合并,最终留下一份独一无二的数据。

它的正常用法如下: 

import java.util.HashSet;  
import java.util.Set;  
public class HashCodeExample {  public static void main(String[] args) {  Set set = new HashSet();  set.add("Java");  set.add("Java");  set.add("MySQL");  set.add("MySQL");  set.add("Redis");  System.out.println("Set 集合长度:" + set.size());  System.out.println();  // 打印 Set 中的所有元素  set.forEach(d -> System.out.println(d));  } } 

3.2 Set 集合的“异常”

然而,如果我们在 Set 集合中存储的是,只重写了 equals 方法的自定义对象时,有趣的事情就发生了,如下代码所示:

import java.util.HashSet;  
import java.util.Objects;  
import java.util.Set;  
public class EqualsExample {  public static void main(String[] args) {  // 对象 1  Persion p1 = new Persion();  p1.setName("Java");  p1.setAge(18);  // 对象 2  Persion p2 = new Persion();  p2.setName("Java");  p2.setAge(18);  // 创建 Set 集合  Set set = new HashSet();  set.add(p1);  set.add(p2);  // 打印 Set 中的所有数据  set.forEach(p -> {  System.out.println(p);  });  }  
}  
class Persion {  private String name;  private int age;  // 只重写了 equals 方法  @Override  public boolean equals(Object o) {  if (this == o) return true; // 引用相等返回 true  // 如果等于 null,或者对象类型不同返回 false  if (o == null || getClass() != o.getClass()) return false;  // 强转为自定义 Persion 类型  Persion persion = (Persion) o;  // 如果 age 和 name 都相等,就返回 true  return age == persion.age &&  Objects.equals(name, persion.name);  }  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 "Persion{" +  "name='" + name + '\'' +  ", age=" + age +  '}';  }  
} 

为了解决上面的问题,我们尝试在重写 equals 方法时,把 hashCode 方法也一起重写了,实现代码如下: 

import java.util.HashSet;  
import java.util.Objects;  
import java.util.Set;  
public class EqualsToListExample {  public static void main(String[] args) {  // 对象 1  Persion p1 = new Persion();  p1.setName("Java");  p1.setAge(18);  // 对象 2  Persion p2 = new Persion();  p2.setName("Java");  p2.setAge(18);  // 创建 Set 对象  Set set = new HashSet();  set.add(p1);  set.add(p2);  // 打印 Set 中的所有数据  set.forEach(p -> {  System.out.println(p);  });  }  
}  
class Persion {  private String name;  private int age;  @Override  public boolean equals(Object o) {  if (this == o) return true; // 引用相等返回 true  // 如果等于 null,或者对象类型不同返回 false  if (o == null || getClass() != o.getClass()) return false;  // 强转为自定义 Persion 类型  Persion persion = (Persion) o;  // 如果 age 和 name 都相等,就返回 true  return age == persion.age &&  Objects.equals(name, persion.name);  }  @Override  public int hashCode() {  // 对比 name 和 age 是否相等  return Objects.hash(name, 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 "Persion{" +  "name='" + name + '\'' +  ", age=" + age +  '}';  }  
} 

3.4 原因分析

出现以上问题的原因是,如果只重写了 equals 方法,那么默认情况下,Set 进行去重操作时,会先判断两个对象的 hashCode 是否相同,此时因为没有重写 hashCode 方法,所以会直接执行 Object 中的 hashCode 方法,而 Object 中的 hashCode 方法对比的是两个不同引用地址的对象,所以结果是 false,那么 equals 方法就不用执行了,直接返回的结果就是 false:两个对象不是相等的,于是就在 Set 集合中插入了两个相同的对象。

但是,如果在重写 equals 方法时,也重写了 hashCode 方法,那么在执行判断时会去执行重写的 hashCode 方法,此时对比的是两个对象的所有属性的 hashCode 是否相同,于是调用 hashCode 返回的结果就是 true,再去调用 equals 方法,发现两个对象确实是相等的,于是就返回 true 了,因此 Set 集合就不会存储两个一模一样的数据了,于是整个程序的执行就正常了。

相关内容

热门资讯

英菲尼迪Q50英菲尼迪Q50最... 今天给各位分享英菲尼迪Q50英菲尼迪Q50最新报价-图片-参数的知识,其中也会对英菲尼迪q50官方报...
如何看中国超燃冲压发动机获得突... 今天给各位分享如何看中国超燃冲压发动机获得突破,地面试验实现连续...的知识,其中也会对中国超燃冲压...
兰博基尼雷文顿多少钱兰博基尼雷... 本篇文章极速百科给大家谈谈兰博基尼雷文顿多少钱兰博基尼雷文顿贵吗?,以及兰博基尼雷文顿跑车图片对应的...
苦恼的反义词(苦恼的反义词最佳... 今天给各位分享苦恼的反义词的知识,其中也会对苦恼的反义词最佳答案进行解释,如果能碰巧解决你现在面临的...
命令行工具检索命令find 和... grep检索文件包含的内容的命令使用 grep 命令可以检索文件包含的内容,例如&#x...
第一章:职场入门:程序员如何开... 作为一名Java程序员,我们深知在当今激烈的市场竞争中,如何开始职业生涯是至关重要的。本章将从多个方...
C语言:文件的读写(fputc... 近段时间,在重新学习一下C语言程序设计,学习到了文件读写这一章节,觉得这方面的知识较复杂,于是把其中...
清华大学土木工程系包含哪些专业... 今天给各位分享清华大学土木工程系包含哪些专业的知识,其中也会对清华大学土木工程系包含哪些专业课程进行...
秦国卫鞅怎么死的(卫鞅最后有没... 今天给各位分享秦国卫鞅怎么死的的知识,其中也会对卫鞅最后有没有娶秦国公主进行解释,如果能碰巧解决你现...
美利达车架号(美利达车架号能查... 今天给各位分享美利达车架号的知识,其中也会对美利达车架号能查出什么信息进行解释,如果能碰巧解决你现在...
马杀鸡什么意思(日语马杀鸡什么... 本篇文章极速百科给大家谈谈马杀鸡什么意思,以及日语马杀鸡什么意思对应的知识点,希望对各位有所帮助,不...
一次 JVM 类加载异常 文章目录1. JVM 类加载异常1. 出现问题2. 解决过程1. JDK 7 版本过老2. JDK ...
Button(按钮)与Imag... 今天给大家介绍的Android基本控件中的两个按钮控件,Button普通按钮和ImageButton...
vue子组件无法根据prop属... 问题描述 在vue中,有一个父组件和一个子组件,在父组件里有一个变量&#...
雪佛兰SPARK是什么车?SP... 今天给各位分享雪佛兰SPARK是什么车?SPARK现在还有卖吗的知识,其中也会对2020雪佛兰spa...
全世界最贵的跑车(全世界最贵的... 今天给各位分享全世界最贵的跑车的知识,其中也会对全世界最贵的跑车是啥进行解释,如果能碰巧解决你现在面...
e哥什么意思(e哥是谁啊) e... 今天给各位分享e哥什么意思的知识,其中也会对e哥是谁啊进行解释,如果能碰巧解决你现在面临的问题,别忘...
推荐国内十大品牌润滑油(国内知... 今天给各位分享推荐国内十大品牌润滑油的知识,其中也会对国内知名品牌润滑油进行解释,如果能碰巧解决你现...
前端性能优化之HTTP缓存 前端缓存 前端缓存可分为两大类:HTTP 缓存和浏览器缓存。 我们今天重点是 HTTP...
Linux 端口号占用如何处理 在Linux中,可以使用以下命令来查看端口号的占用情况: sudo ne...
再探pytorch的Datas... 本文从分类、检测、分割三大任务的角度来剖析pytorch得dataset和dataloader源码&...
电影最爱剧情详细介绍,最爱电影... 电影最爱剧情详细介绍目录电影最爱剧情详细介绍最爱电影剧情最爱这部电影讲述的是啥情节电影最爱剧情详细介...
公斤力什么单位(公斤力等于多少... 今天给各位分享公斤力什么单位的知识,其中也会对公斤力等于多少公斤进行解释,如果能碰巧解决你现在面临的...
汽车压缩比是什么意思(汽车压缩... 今天给各位分享汽车压缩比是什么意思的知识,其中也会对汽车压缩比的定义进行解释,如果能碰巧解决你现在面...
小巧实惠又时尚7款市场在售微型... 本篇文章极速百科给大家谈谈小巧实惠又时尚7款市场在售微型电动车,以及微型电动车推荐对应的知识点,希望...
cdn服务器搭建步骤 CDN服务器是现代网络中不可或缺的一部分,其可以大大提高网站的访问速度和用户体验。许多...
Go项目(分布式事务) 文章目录简介分布式事务CAPBASE常见方案 简介 目前,项目的主要代码已经开发完毕&...
leetcode每日一题:45... 系列:贪心算法 语言:java 题目来源:Leetcode...
差速器工作原理是什么(差速器工... 本篇文章极速百科给大家谈谈差速器工作原理是什么,以及差速器工作原理是什么意思对应的知识点,希望对各位...
幸福花园纤细的爱故事内容是什么... 幸福花园纤细的爱故事内容是什么 目录幸福花园纤细的爱故事内容是什么 幸福花园有几部求幸福花园的第二个...