【java】源码配合实践一文弄清楚为什么重写equals一定要重写hashcode

源码配合实践一文弄清楚为什么重写equals一定要重写hashcode

简介

本文会带各位从源码的角度以及实际应用案例探究为什么重写equals一定要重写hashcode。

实践

考虑以下场景,现在有一个学生管理系统,每个学生有身份证号,姓名,年龄以及性别四个属性。

package com.equalsAndHashCode;

import java.util.Objects;

/**
 * 学生
 *
 * @author ez4sterben
 * @date 2023/07/18
 */
public class Student {
    
    

    private String id;
    private String name;
    private Integer age;
    private String sex;

    @Override
    public String toString() {
    
    
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }

    public Student(String id, String name, Integer age, String sex) {
    
    
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public String getId() {
    
    
        return id;
    }

    public void setId(String id) {
    
    
        this.id = id;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public Integer getAge() {
    
    
        return age;
    }

    public void setAge(Integer age) {
    
    
        this.age = age;
    }

    public String getSex() {
    
    
        return sex;
    }

    public void setSex(String sex) {
    
    
        this.sex = sex;
    }
}

在现实生活当中,难免会出现姓名年龄性别完全相同的两个人,如果我们要在数据中区分他们,那么就需要使用身份证号来区分。

那么我们想判断学生管理系统中的两个学生是否是同一个学生,是不是只需要判断身份证号是否相同就可以了?

那么接下来我们重写一下equals方法,来判断两个学生是否相同。

    @Override
    public boolean equals(Object o) {
    
    
        if (this == o) {
    
    
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
    
    
            return false;
        }
        Student student = (Student) o;
        return id.equals(student.id);
    }

再写一个测试类来查看是否生效。

package com.equalsAndHashCode;

/**
 * 测试
 *
 * @author ez4sterben
 * @date 2023/07/18
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        Student student1 = new Student("100000202307181024","李华",1,"女");
        Student student2 = new Student("100000202307181025","张三",1,"男");
        System.out.println(student1.equals(student2));
        
        student2.setId("100000202307181024");
        System.out.println(student1.equals(student2));
    }
}

运行结果如下:

在这里插入图片描述
可以看到我们重写的equals方法已经生效,只要身份证号不同那么这两个学生就是不同的。

到这里大家可能会想:测试成功了为什么还需要重写hashcode?

别着急,考虑以下场景:现在教师需要将所有的学生存入到数据集中,方便输出查看。

看到这个需求我们就可以分析一下,学校中的每个学生当然是不同的了,那么存储不同数据要使用的数据结构就应该是HashSet。

接下来我们实现这个需求

    package com.equalsAndHashCode;

    import java.util.HashSet;
    import java.util.Set;

    /**
     * 测试
     *
     * @author ez4sterben
     * @date 2023/07/18
     */
    public class Test {
    
    
        public static void main(String[] args) {
    
    
            Student student1 = new Student("100000202307181024","李华",1,"女");
            Student student2 = new Student("100000202307181025","张三",1,"男");

            Set<Student> students = new HashSet<>();
            
            students.add(student1);
            students.add(student2);

            System.out.println(students);
        }
    }

查看结果如下:

在这里插入图片描述

现在我们满足了教师的需求,但是现在问题来了,有一名学生在网络提交信息的时候把名字写错了,提交了多次数据。不过不用担心,我们使用的是HashSet,应该只有一份数据,不会出现多个相同id学生的问题。

我们让张三同学来承担一下这个责任

    package com.equalsAndHashCode;

    import java.util.HashSet;
    import java.util.Set;

    /**
     * 测试
     *
     * @author ez4sterben
     * @date 2023/07/18
     */
    public class Test {
    
    
        public static void main(String[] args) {
    
    
            Student student1 = new Student("100000202307181024","李华",1,"女");
            Student student2 = new Student("100000202307181025","张三",1,"男");
            Student student3 = new Student("100000202307181025","张四",1,"男");

            Set<Student> students = new HashSet<>();

            students.add(student1);
            students.add(student2);
            students.add(student3);

            System.out.println(students);
        }
    }

结果却是出人意料

在这里插入图片描述
我们的HashSet里居然有两个相等身份证号的同学出现了。而相同身份证号命名是判等才对的,HashSet不是无序不可重复的吗?

下面通过源码来带大家分析一下

源码解析

下面我们查看HashSet的源码看看到底为什么会发生这种情况。

首先我们调用了HashSet的构造方法

在这里插入图片描述

但他的内容是创建一个新的HashMap

在这里插入图片描述

原来HashSet是借助HashMap实现的,我们来看看他的add方法,通过idea中的Structure可以看到类的结构

在这里插入图片描述
在这里插入图片描述

到这里我们可以发现,这个add方法,调用的也是map的put方法。以传入的元素作为Key,PRESENT作为Value,而PRESENT其实是一个空对象

在这里插入图片描述

下面来看看put方法

在这里插入图片描述
在这里插入图片描述

从这里可以看出来,HashMap对于节点的判断其实是利用了hash函数,而这个hash函数是借助了传入的Key的Hash函数

在这里插入图片描述

所以说,如果我们只重写了equals没有重写hashcode,就会发生与期望结果不相符的情况。

接下来我们重写一下hashcode

    @Override
    public int hashCode() {
    
    
        return Objects.hash(id);
    }

老规矩,身份证号一样就行。

再次运行

在这里插入图片描述

这样就满足我们的预期了,只要身份证号一样,就认为这个学生已经存在了。

猜你喜欢

转载自blog.csdn.net/qq_51383106/article/details/131781193