首页 / JAVA  

重写equals()方法的同时必须要重写hashcode()方法?


== :主要用于基本类型之间的比较(char、Boolean、byte、short、int、long、float、dobule),也可以用于比较对象
equals: 对象之间的比较(基本类型的包装器类型,string,自己定义的对象等)

基本类型的包装器类型、string已经帮我们重写了equals方法,所有他们比较的是值。但是我们自定义的对象比较还是集成了Object的equals方法

所以不重写equals方法比较是没有意义的;因为它们始终比较的值对象的引用地址。

对于一个对象student来说,如果我们不重写它的equals方法,那么和==符号一样比较的是对象的引用而不是内容

public class Student {
private int id;
private String name;
private String password;


public Student(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
}

Student s1 = new Student(1, "A", "123456");
Student s2 = new Student(1, "B", "123456");

System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//false

上面两个对象s1和s2不相等,因为他们指向的是两个不同的对象,所以引用不同,但是我们的目的是要达到如果id,name,password都相同,那么就是同一个对象,所以需要重写equals()方法

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Student student = (Student) o;

if (id != student.id) return false;
if (name != null ? !name.equals(student.name) : student.name != null) return false;
return password != null ? password.equals(student.password) : student.password == null;
}
这个时候:

Student s1 = new Student(1, "A", "123456");
Student s2 = new Student(1, "B", "123456");

System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//true
讨论一下重写equals()方法的同时必须要重写hashcode()方法吗

​  首先我们重写equals()的目的就是为了让内容相同的对象让它们相等,而不是单单只比较对象的引用地址,如果是比较地址那两个对象永远不可能相等;也就是尽管这两个对象的引用地址不同,但是只要它们的内容(所有的属性值)相同,我们调用equals方法的时候仍然返回true。

那么这个时候我们为什么又要重写hashcode方法呢,hashcode()返回的是对象的地址,是一个散列值,那么如果我们通过equals()方法得到这两个对象相同,尽管他们在堆中的内存地址不一样,但是我们希望他们的哈希值是一样的,这样如果存入map的话,就能定位到相同的索引

  • 同时Java标准中对hashcode有如下的规定:

    • 在java应用程序执行期间,如果在equals方法比较中所用的信息没有被修改,那么在同一个对象上多次调用hashCode方法时必须一致地返回相同的整数。如果多次执行同一个应用时,不要求该整数必须相同。
    • 如果两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。
    • 如果两个对象通过调用equals方法是不相等的,不要求这两个对象调用hashCode方法必须返回不同的整数。


如果我们不重写student的hashcode()方法,那么就会默认调用object的hashcode()方法:

public static void main(String[] args) {

Student s1 = new Student(1, "A", "123456");
Student s2 = new Student(1, "B", "123456");
System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//true
System.out.println(s1.hashCode());//356573597
System.out.println(s2.hashCode());//1735600054
}
我们可以看到以上的运行结果违背了hashcode的规定:如果equals()返回true,那么hashcode方法必须返回相同的整数

所以我们需要对student对象的hashcode方法进行重写

@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
return result;
}
通过重写hashcode()让其与对象的属性关联起来,那么就能够达到equals()为true,那么hashcode的值也相等。

现在我们已经知道了重写equals()方法的同时需要重写对象的hashcode()方法,让其满足hashcode的标准条件。

为什么hashcode需要这样定义标准呢,

这样做到底有什么好处呢,这里我们就需要提到map类了,我们知道hashmap的结构是一个数组加链表组成的。

它的存储原理就是通过存入的value值对象的hashcode % capacity 计算该value在hashmap数组中的索引,然后将该(key,value)放入该索引对应的链表里面;

Student s1 = new Student(1, "A", "123456");
Student s2 = new Student(1, "B", "123456");

对于s1和s2两个对象,如果我们我们已经将s1存入一个map对象,那么我们再存入s2时,我们希望的是这是不能再插入map了,因为此时map中已经存在小王这个对象了,那么如何才能做到呢

首先我们通过s1的hashcode % capacity 得到了一个数组索引,然后将s1这个对象存入map,那么我们再插入s2的时候同样也需要计算它的hashcode,然后定位到相同的数组索引,然后判断该链表中是否存在小王这样一个对象,如果存在就不put

所以我们需要得到的s1和s2的hashcode相同,才能避免同一个对象被put进入map中多次,所以我们才需要在重写equals()方法的同时重写hashcode()方法,让两个相等的对象具有相同的hashcode
2019-10-20