博客 分类专栏 专题 成员
Java中重写equals就必须重写hashCode吗?
2022-02-09 11:06:29
分类专栏: Java

事件

今天同事跑过来问我,我用list判断是否包含对象,我只重写了equals方法,居然可以正确判断,不是说重写equals就必须重写hashCode么?我让她看看ArrayList的 contains 方法,然后她就明白了。

== :它计算的是操作数的值之间的关系,如果是基本数据类型,值相等即可,如果是引用类型,内存地址要一样
equals : Object 的 实例方法,比较两个对象的content是否相同
hashCode : Object 的 native方法 , 获取对象的哈希值,用于确定该对象在哈希表中的索引位置,它实际上是一个int型整数

文字表达太空洞了,我们直接上代码

不重写 equals 也不重写 hashCode

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

    private String name;
    private Integer age;

    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 static void main(String[] args) {
        Student student1 = new Student("张三", 1);
        Student student2 = new Student("张三", 2);
        System.out.println("==:" + (student1 == student2));
        System.out.println("equals:" + (student1.equals(student2)));
        System.out.println("student1 hashCode:" + student1.hashCode() + ",student2 hashCode:" + student2.hashCode());
        Set<Student> set = new HashSet<>();
        set.add(student1);
        set.add(student2);
        System.out.println("set的大小:" + set.size());
        List<Student> list = new ArrayList<>();
        list.add(student1);
        System.out.println("list contains:" + list.contains(student2));
    }
}

运行结果:

==:false
equals:false
student1 hashCode:30852576,student2 hashCode:20929316
set的大小:2
list contains:false

没有重写equals 所以也是false
hashSet 无序,不能重复,大小是2说明 是两个对象
list 是有序集合,可重复 包含 是两个对象,所以是false

只重写 equals 不重写 hashCode

此时equals等,hashCode不等

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

    private String name;
    private Integer age;

    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;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj.getClass() != this.getClass()) {
            return false;
        }
        return this.getName().equals(((Student) obj).getName());
    }
    public static void main(String[] args) {
        Student student1 = new Student("张三", 1);
        Student student2 = new Student("张三", 2);
        System.out.println("==:" + (student1 == student2));
        System.out.println("equals:" + (student1.equals(student2)));
        System.out.println("student1 hashCode:" + student1.hashCode() + ",student2 hashCode:" + student2.hashCode());
        Set<Student> set = new HashSet<>();
        set.add(student1);
        set.add(student2);
        System.out.println("set的大小:" + set.size());
        List<Student> list = new ArrayList<>();
        list.add(student1);
        System.out.println("list contains:" + list.contains(student2));
    }
}

==:false
equals:true
student1 hashCode:30852576,student2 hashCode:20929316
set的大小:2
list contains:true

equals 肯定是true,因为重写了equals方法,name值一样,所以是true
hashCode 不一样 hashSet 也认为是两个值
list 是包含的,我们看下ArryList contains 实现
image.png
对比的就是equals方法,所以集合判断是否包含只需重写 equals 就可以了

只重写 hashCode 不重写 equals

此时 hashCode等,但是equals不等

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

    private String name;
    private Integer age;

    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;
    }

   /* @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj.getClass() != this.getClass()) {
            return false;
        }
        return this.getName().equals(((Student) obj).getName());
    }*/

    @Override
    public int hashCode() {
        return getName().hashCode();
    }

    public static void main(String[] args) {
        Student student1 = new Student("张三", 1);
        Student student2 = new Student("张三", 2);
        System.out.println("==:" + (student1 == student2));
        System.out.println("equals:" + (student1.equals(student2)));
        System.out.println("student1 hashCode:" + student1.hashCode() + ",student2 hashCode:" + student2.hashCode());
        Set<Student> set = new HashSet<>();
        set.add(student1);
        set.add(student2);
        System.out.println("set的大小:" + set.size());
        List<Student> list = new ArrayList<>();
        list.add(student1);
        System.out.println("list contains:" + list.contains(student2));
    }
}

运行结果:

==:false
equals:false
student1 hashCode:774889,student2 hashCode:774889
set的大小:2
list contains:false

跟equals相关的都是false了,此时hashCode等,但是equals 不等 hash对比的也认为是两个对象,我们用HashMap,和 HashSet都可以测试。

重写 equals 和 hashCode

此时 equals等,是hashCode等

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

    private String name;
    private Integer age;

    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;
    }

   @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj.getClass() != this.getClass()) {
            return false;
        }
        return this.getName().equals(((Student) obj).getName());
    }

    @Override
    public int hashCode() {
        return getName().hashCode();
    }

    public static void main(String[] args) {
        Student student1 = new Student("张三", 1);
        Student student2 = new Student("张三", 2);
        System.out.println("==:" + (student1 == student2));
        System.out.println("equals:" + (student1.equals(student2)));
        System.out.println("student1 hashCode:" + student1.hashCode() + ",student2 hashCode:" + student2.hashCode());
        Set<Student> set = new HashSet<>();
        set.add(student1);
        set.add(student2);
        System.out.println("set的大小:" + set.size());
        List<Student> list = new ArrayList<>();
        list.add(student1);
        System.out.println("list contains:" + list.contains(student2));
    }
}

运行结果

==:false
equals:true
student1 hashCode:774889,student2 hashCode:774889
set的大小:1
list contains:true

此时 hash对比的才认为是一个对象,hash对比的操作 必须重写 equals 和 hashCode

总结

在重写 equals 的时候,我们可以根据实际情况来判断,并非说 重写 equals 就必须重写 hashCode
如果是 集合对比,重写 equals 即可
如果是 hash对比,比如 HashMap,HashSet 就需要重写 equals和hashCode 了。

我说的可能不对哈,请高人在评论区指出我的错误。