在回答这个问题前,我们先来看看Object类中的这两个方法:
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
其中hashCode调用的是本地方法,如果子类补充下默认调用的是本地方法。Java平台有个用户和本地C代码进行互操作的API,称为Java Native Interface (Java本地接口)。
equals方法比较的是内存地址是否相等,因此一般情况下也无法满足两个对象值的比较。
下面做个试验,用同一个Student类创建两个实例对象,然后比较各种情况下,这两个对象是否相等。
1、先将Student中的hashCode()方法注释掉
package com.school.eution.accommodation;
import java.util.Objects;
public class Student {
private int age;
private String 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;
}
public Student() {
super();
}
public Student(int age, String name) {
super();
this.age = age;
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);
}*/
}
进行如下测试:
package com.school.eution.accommodation;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Student s1 = new Student(1,"June");
Student s2 = new Student(1,"June");
Map
多次运行结果一致:
971848845
美国学生
------------------
1910163204
null
------------------
true
解释:如果不重写hashCode方法就会使用从Object继承来的本地hashCode()方法,所以将对象s1放入Map中后,通过s1对象能从Map中取出来字符串“美国学生”,但是s2和s1在值上是相等的,却不能使用s2对象从Map中也获取到同样的值。就是因为没有重写hashCode方法导致使用默认hasCode产生的Hash值不一致造成的。所以s2取值的时候按照s2的hash值“1910163204”就在Map中找不到。
但是如果不使用Map取值,直接通过equals比较是相等的(此时并没有重写hashCode)。
Map取值是先通过hash值定位,如果hash值存在于Map中,并且hash值一致就说明在Map的同一个链表中,继续使用equals方法比较值是否相等,如果hash值都不一致,就没必要往下比较了直接返回false。
下面将Student中重新hash的代码放开
package com.school.eution.accommodation;
import java.util.Objects;
public class Student {
private int age;
private String 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;
}
public Student() {
super();
}
public Student(int age, String name) {
super();
this.age = age;
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);
}
}
再次运行结果:
2321474
美国学生
------------------
2321474
美国学生
------------------
true
因为重新了hashCode,此时,s1与s2的age与name值又是相等的,所以使用对象s1、s2都能从Map中取得相同的值。即使s2事先并没有向Map中存放,也就是可以使用s2与s1Hash值一样,来用s2获取s1存放到Map中的值。就好比s1与s2拿了同一把钥匙。
通过equals方法比较s1与s2依然是相等的。
总结:为什么一定要同时重写hashCode()方法和equals()方法,是针对HashSet和HashMap等这类使用hash值存储的对象而言的。比如:在向HashSet存放Student时,如果没有重写hashCode,这时往HashSet中存放s1、s2实际上是能存放两份的,即使s1与s2的值完全一致。但如果用不到这些Hash的集合,只重写equals()方法也能满足像个对象值是否相等的比较。