为什么重写了equals方法后,也要重写hashCode方法?

立山  论坛元老 | 2024-10-18 11:26:03 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1027|帖子 1027|积分 3081

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
equals方法:

一、根据Java1.8官方文档中对与equals方法的形貌:​​​​​​​

equals方法属于Object类(根类)。指示其他对象是否“等于”这个对象。
等式方法实现了非空对象引用的等价关系:
1. 它是自反的:对于任何非空引用值x,x.equals(x)应该返回true。
2. 它是对称的:对于任何非空引用值x和y,x.equals(y)应该返回true,当且仅当y.equals(x)返回true。
3. 它是传递的:对于任何非空引用值x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)应返回true。
4. 它是一致的:对于任何非空引用值x和y,x.equals(y)的多次调用一致返回true或始终返回false,前提是不修改用于对象等比较的信息。
5. 对于任何非空引用值x:x.equals(null)应返回false。
类Object的相称方法实现了对象上最具辨别性的等价关系;也就是说,对于任何非空引用值x和y,此方法返回true,当且仅当x和y引用同一对象(x == y的值为true)。
请留意,每当此方法被重写时,通常都需要覆盖hashCode方法,以维护hashCode方法的一样平常合同,该合同规定相称对象必须具有相称的哈希代码。
二、根据源码:


 *通过以上两点我们可以发现equals方法的本质是用于比较两个对象的内存所在是否雷同。

让我们通过一个例子明白equals方法:


        当o1/o2均为 new 出来的对象时,它们在 堆内存 中拥有两块 差别的空间,此时o1和o2两个引用(存在于栈内存)分别指向两个差别的堆内存所在。这时调用equals方法返回的结果为 false :

        当o2为o1对象的引用复制时,o2引用同样指向o1对象,此时o1/o2两个引用 对应雷同的所在,而0x1001(原o2对象)对象由于不再有引用指向它,将被 垃圾接纳器 销毁,此时调用equals方法的返回值为 true:


三、我们可以通过String类对equals方法的重写加深明白:


在String类对equals方法的重写源码中我们可以看到:起首对于 所在 进行判断,如果两个对象的所在雷同,则返回 true,否则判断两个字符串的 值是否雷同 ,如果值雷同则返回 true,反之亦然。

 hashCode方法:

我们需要知道,在 Java 中的任何一个对象都包含一个 native 的 hashCode 方法:

*这是Object类对于hashCode方法的定义 

        在散列存储结构中,我们在新增元素时需要判断新增的元素是否已经存在于集合中,如果利用equals方法,效率过于低下,以是利用hashCode办理这个问题:如果集合中不存在与新增元素雷同的hashCode值,则将新元素直接参加存储结构中;如果集合中存在与新增元素雷同的hashCode值则调用equals方法进行进一步的比较,如果雷同则覆盖,如果不雷同则散列到其他所在,这使得调用equals方法的概率大大低落提高了存储速度。
        hashCode就是 对象的哈希码,默认环境下hashCode返回的是对象的存储所在映射成一个数值,也可以重写hashCode方法来实现本身的哈希码逻辑,通过哈希码可以提高查询的效率,重要用于在哈希表(散列表)结构中快速确定对象的存储所在,如HashMap、HashSet中。
需要留意的是:
        1. 由于hashCode是JVM利用随机数生成,以是两个差别的对象可能会产生雷同的hashCode值,可能造成哈希冲突问题。
        2. 如果两个对象完全雷同,即它们的内存所在雷同,则它们的hashCode值一定雷同

结合分析:

我们先建立一个只重写equals方法的Student类:

  1. import java.util.Objects;
  2. public class Student {
  3.     private String name;
  4.     private int age;
  5.     public Student(String name, int age) {
  6.         this.name = name;
  7.         this.age = age;
  8.     }
  9.     public String getName() {
  10.         return name;
  11.     }
  12.     public void setName(String name) {
  13.         this.name = name;
  14.     }
  15.     public int getAge() {
  16.         return age;
  17.     }
  18.     public void setAge(int age) {
  19.         this.age = age;
  20.     }
  21.     /*Attention!*/
  22.     @Override
  23.     public boolean equals(Object o) {
  24.         if (this == o) return true;
  25.         if (o == null || getClass() != o.getClass()) return false;
  26.         Student student = (Student) o;
  27.         return age == student.age && Objects.equals(name, student.name);
  28.     }
  29.    
  30. }
复制代码
        对于equals方法,我们先对内存所在进行判断,如果内存所在差别,再根据年龄和姓名的值进行判断。
在Main类中测试:

  1. public class Main {
  2.     public static void main(String[] args) {
  3.         Student std1 = new Student("test", 18);
  4.         Student std2 = new Student("test", 18);
  5.         System.out.println("std1 & std2 are the same: " + std1.equals(std2));
  6.         Set<Student> students = new HashSet<>();
  7.         students.add(std1);
  8.         students.add(std2);
  9.         for (Student student : students) {
  10.             System.out.println("name: " + student.getName() + ", age: " + student.getAge());
  11.         }
  12.     }
  13. }
复制代码
        创建std1和std2对象,利用构造方法赋初值(name = test,age = 18)。我们起首调用equals方法,判断两个对象是否雷同,由于在Student类中重写了equals方法 ,故equals的返回值应为 true。我们继续将两个Student对象(std1、std2)参加到Set集合中(set集合为散列存储结构,而且不允许出现重复元素)。末了我们迭代这个Set集合(students),不停输出集合中的内容,由于两个对象先前调用过equals方法,说明这两个对象在 逻辑上 应该为 雷同 的对象,不应该在集合中重复出现。
我们运行步伐检察结果:

  1. std1 & std2 are the same: true
  2. name: test, age: 18
  3. name: test, age: 18
复制代码
        两个雷同的对象居然同时出如今了Set集合中!继续分析,我们利用new关键字创建了两个Student对象,根据先前的介绍,这两个对象在堆内存中位于差别的所在,而hashCode值又与所在有关,以是这两个对象的hashCode值差别,而在散列存储结构中添加元素时,起首判断新元素的hashCode值是否在集合中存在,当前环境下显然是不存在,Set集合将雷同的“新”元素添加进了存储结构中。
故继续重写hashCode方法:(在Studen类中添加以下代码)

  1. @Override
  2.     public int hashCode() {
  3.         return Objects.hash(name, age);
  4.     }
复制代码
          此处的hashCode方法利用Objects中提供的hash方法,其本质如下:

 再次在Main类中测试:

  1. std1 & std2 are the same: true
  2. name: test, age: 18
复制代码
        结果与逻辑符合合,问题乐成办理了。

总结:

        如果只重写equals方法,不重写hashCode方法,就有可能导致在 x.equals(y) 表达式成立的条件下,x 和 y 的 hashCode 值却不雷同。此时这个只重写了 equals 方法的对象在利用散列集合进行存储的时候,由于散列集合利用 hashCode 来确定 key 的位置,如果存储两个完全雷同的对象但是这两个对象有差别的hashCode值,就会出现两个雷同的对象储存在散列集合的差别位置,违反了散列集合的规则,也会造成该类对象无法利用散列存储结构。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

立山

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表