为什么重写equals 和 hashcode 方法,lombok中@EqualsAndHashCode(callSuper = false/true) 什么区别
前言
一开始学习 java 的时候,当我们在定义一个 pojo 的时候,都会去重写 equals 和 hashcode 方法。我已经忘记了当时是怎么学习的,反正感觉当时并没有很清晰的认知到重写equals 和 hashcode 的意义是什么,只是简单的背了一些八股文,包括在学习 Map的时候,也并没有很关心这一点。
目前已经工作一年时间了,平常直接一个lombok的@Data注解就搞定一个 pojo ,所以今天好好的复习一下
为什么要重写 equals 方法
java中的类都隐式的继承于Object这个父类,在 Object 类中就有 equals 和 hashcode 方法。在Object 类中的 equals 方法,是直接比较引用值:
public boolean equals(Object obj) {
return (this == obj);
}
思考下面一个情景,当我们有一个类 Student ,里面有 name,age属性如下:
public class Student {
private String name;
private Integer age;
// ......
}
那么我使用 new 关键字创建两个学生对象时:
new Student("zhangsan",12)
new Student("zhangsan",12)
然后使用 equals方法去比较,这个时候比较的是对象的引用值,而这两个对象的引用值肯定是不等的,所以结果是 false,可是这和我们实际业务,或实际认知中的有矛盾,我明明同一个类的两个对象的属性值都一样,使用equals 方法比较的结果却是 false,为了解决这个问题,所以需要对 equals方法进行重写。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return Objects.equals(name, student.name) && Objects.equals(age, student.age);
}
为什么重写了 equals 方法一定要重写 hashcode 方法
在 java中有通用的约定:
- 在java应用程序运行时,无论何时多次调用同一个对象时的hashCode()方法,这个对象的hashCode()方法的返回值必须是相同的一个int值。
- 如果两个对象equals()返回值为true,则他们的hashCode()也必须返回相同的int值。
- 如果两个对象equals()返回值为false,则他们的hashCode()返回值也必须不同。
上面是一种约定(可以说是强制的),是为了正确的处理 java 中如散列数据结构存储对象
hashcode 值是有限的,也就是不同的对象,可能会有相同的 hashcode 值,这也就是发生了 hash冲突。
我们都知道Set 首先会通过对象的hashcode 定位数组的索引,如果这个位置没有元素则直接放入,如果有元素即发生了hash冲突,则使用equals方法比较,为true则丢弃,为false则形成链表插入。
如果重写了equals方法,而没有重写 hashcode方法,就可能造成,把两个相等的对象全部存入 Set 中,即散列到不同的位置(由于只重写了equals 方法,没有重写hashcode,则有可能两个对象相等,但是hashcode值不同),这是矛盾的(Set 不能存储相同元素),正确的应该是相同对象散列到相同的位置,所以重写equals 必须重写hashcode
lombok 中@EqualsAndHashCode(callSuper = false/true) 什么区别
其实就是当一个类继承自一个父类时,如果callSuper=true,那么生成的equals和 hashcode方法会讲父类中的属性包括,默认callSuper =false
@Data
@EqualsAndHashCode(callSuper = false)
public class EHSubClass extends EHSuperClass{
private String name;
private String addr;
}
@Data
public class EHSuperClass {
private Boolean isDel;
public static void main(String[] args) {
EHSubClass ehSubClass = new EHSubClass();
ehSubClass.setAddr("beijing");
EHSubClass ehSubClass1 = new EHSubClass();
ehSubClass1.setAddr("beijing");
ehSubClass1.setIsDel(false);
// callSuper =false 时结果为true, callSuper =true时,结果为false
System.out.println(ehSubClass1.equals(ehSubClass));
}
}