为什么需要重写equals方法?
比方创建一个Student类,定义了一个全参构造方法
public class Student {
private String name;
private Integer stuId;
public Student(String name, Integer stuId) {
this.name = name;
this.stuId = stuId;
}
}
这时需要根据学生的姓名和唯一学号判断是不是同一个人
public static void main(String[] args) {
Student stu1 = new Student("张三", 20);
Student stu2 = new Student("张三", 20);
System.out.println(stu1.equals(stu2));
}
逻辑上可以根据学生的姓名和学号是否相等来判断是不是同一个人,那么这时stu1和stu2就应该是一个人,即stu1.equals(stu2)打印的结果是true才符合逻辑,看看结果
为什么打印的结果却为false?我们查看一下Student类的equals方法
public boolean equals(Object obj) {
return (this == obj);
}
可以看到Student类的equals方法是继承的Object类,即stu1.equals(stu2)实际上是返回的stu1==stu2的值,==运算符是比较两个对象的内存地址值,stu1和stu2是指向的两个不同的对象,所以他们的内存地址值肯定不一样
这种情况下,是不是需要自己重写Student中的equals方法才能判断出stu1和stu2是否相等?
通过idea的自动生成equals方法,再看看现在的Student类
public class Student {
private String name;
private Integer stuId;
public Student(String name, Integer stuId) {
this.name = name;
this.stuId = stuId;
}
@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(stuId, student.stuId);
}
}
equals首先判断两个对象的内存地址是否相同,再判断是否是同一地址,最后再判断里面的属性值是否相等,所以这时再执行stu1.equals(stu2),结果就是true了,在以后逻辑中就可以通过姓名和学号判断是否是同一个学生了
为什么重写了equals方法后要重写hashcode方法?
我们知道,在HashMap里面,如果key重复的话,就会覆盖以前的值,那么看下面代码简单测试一下
再看一下用Student做为key时的打印结果
public static void main(String[] args) {
Student stu1 = new Student("张三", 20);
Student stu2 = new Student("张三", 20);
Map<Student, Integer> map = new HashMap<>();
map.put(stu1, 1);
map.put(stu2, 2);
System.out.println(map);
}
上面代码stu1.equals(stu2)相等,也是我们认为的逻辑上相等,那么打印的结果,也应该是下面的操作把上面的值覆盖了才合理,看看打印结果
这里竟然打印了两个值, 不合理啊,为什么不合理呢?
因为在HashMap中,是根据key的hashcode值确定数组下标位置的,如果数组下标位置相同,先比较key的hashcode值,如果相同,再用equals方法对比,如果相等,则覆盖,所以刚刚map.put(1,2)能够覆盖第一个操作,是因为他们的key都是1,所以他们的hashcode是相等,同样hashmap计算他们的数组下标位置也是相同的
所以我们看看我们的stu1和stu2的hashcode值是否相等
发现他们的值并不一样,我们看看hashcode方法源码
public native int hashCode();
这是一个本地方法,它的实现与本地机器有关,这里我们暂且认为他返回的是对象存储的物理位置。
stu1和stu2本来就是在堆内存中的两个不同的对象,他们的物理地址值肯定不相同了,但是他们的name属性和stuId相等,在我们的逻辑里相等的,我们需要他们equals相等,重写了equals方法,现在需要他们hashcode也相等,是不是应该也重写他们的hashcode方法?
利用idea自动重写hashcode后Student类如下
public class Student {
private String name;
private Integer stuId;
public Student(String name, Integer stuId) {
this.name = name;
this.stuId = stuId;
}
@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(stuId, student.stuId);
}
@Override
public int hashCode() {
return Objects.hash(name, stuId);
}
}
再打印一下他们的hashcode值看看
发现hashcode值相等了,我们再试试map里面能否覆盖
发现覆盖成功了,所以现在知道为什么重写了equals方法后必须要重写hashcode方法了吧
总结
在java里面,如果两个对象的hashcode相等,他们equals对比却不一定相等,如果两个对象equals对比相等,他们的hashcode值一定相等
所以不光在HashMap里面是先比较hashcode值再比较equals,在HashTable或HashSet里面也是这样的逻辑,因为先比较hashcode仅为一个int值,比较的效率更高,可以更快速的排除不相等的情况。