一文搞定 equals 和 hashCode

用户头像
shengjk1
关注
发布于: 2020 年 04 月 23 日

主要是说一下 equals 和 hashcode

HashCode

  1. 此方法主要是用来支持 hash tables。比如 HashMap

  2. 要求在 equals 不改动的情况下,每次调用得到的 hashCode 必须是一致的。

  3. 两个对象 equals 相等,则 hashCode 也应该相等。

  4. equals 不相等,则不必要求 hashCode 一定不同,应尽量不同以保证高性能。

  5. 默认实现,通常是通过对象的内部地址转换为整数,所以大多数情况下 Object 定义的 hashCode 确实可以为不同的对象返回不同的整数。

Equals

  1. 对于 non-null reference x ,满足 x.equals(x) return true。

  2. 对于 non-null reference x,y 满足 对称性 即:如果 x.equals(y) return true ,则 y.equals(x) 也会 return true 。

  3. 对于 non-null reference x,y,x 满足 传递性。即:如果 x.equals(y) return true 和 y.equals(z) return true,那么 x.equals(z) 也应该 return true 。

  4. 对于任何 non-null reference x,y,满足一致性,x.equals(y) 要是 true永远是true

  5. 对于任何 non-null reference x, x.equals(null) return false.

  6. 当重写 equals 方法是必须需要重写 hashCode,这样才能保证 equals 为true的两个对象的 hashCode也一样。(jre 中就有违背这条的类存在)

  7. equals 默认是比较的内存地址。所以默认情况下,当 equals 相等的时候,hashCode也是相等的。

什么时候应该覆盖 equals

我们知道 equals 默认比较的内存地址,即是否为同一个对象。如果类具有了自己特有的逻辑相等,即属性值等,的概念时,并且超类没有覆盖或者覆盖了单不满足子类需求的情况下重写 equals。



但对于类似枚举类这样的 class 而言,每个属性最多只存在一个对象,逻辑相等与内存地址相等时一样的,不需要重写。

为什么要覆盖 equals( hashCode)

/*
@author shengjk1
@date 2020/3/14
/
public class TestEquals {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap<>();
Student student = new Student(16, "小明");
map.put(student, "a");
Student student1 = new Student(16, "小明");
System.out.println(map.containsKey(student1));
}
}
class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// @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(age, name);
}
}

结果

false

这完全不是我们想要的,我们希望的是 map.containsKey(student1) 为true。但为什么是 false 呢?通过源码我们可以知道 应为 equals 不等,导致的。同理,对于 hashCode 也是如此。

任何时候都需要覆盖 equals 和hashCode

答案是 否定的。

还记得上一篇说过的吗,JDK 内部就有违背 equals hashCode 一致性规则的。另外的话,hashCode 仅仅针对 hash 表来说才有用。

/*
@author shengjk1
@date 2020/3/14
/
public class TestEquals {
public static void main(String[] args) {
HashMap<Student, String> map = new HashMap<>();
Student student = new Student(16, "小明");
map.put(student, "a");
Student student1 = new Student(16, "小明");
System.out.println("map " + map.containsKey(student1));
ArrayList<Student> students = new ArrayList<>();
students.add(student);
System.out.println("list " + students.contains(student1));
}
}
class Student {
private int age;
private String name;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@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(age, name);
// }
}

结果

map false
list true

对于 list 而言,根本就不需要 比较hashCode,我们也违背了 equals 与 hashCode 的一致性,但对于 students.contains(student1) 结果就是正确的

总结

1.java 集合内部比较对象是否相等一般用的都是 equals ,所以 equals 是要尽量重写的。

2.而对于 hashCode 来说,对于 hash table 来说是必须要重写的,其他的可以不重写。



发布于: 2020 年 04 月 23 日 阅读数: 43
用户头像

shengjk1

关注

还未添加个人签名 2018.04.26 加入

博客 https://blog.csdn.net/jsjsjs1789

评论

发布
暂无评论
一文搞定 equals 和 hashCode