重写equals时必须重写hashCode方法

宋正兵 on 2020-11-16

- 阿兵,你知道重写equals和hashCode吗?

- 害,那玩意儿我能不知道?重写equals的时候必须要重写hashCode方法。

- 那为啥呢?为啥俩就需要一起重写?

- 你来找茬的吧。

尴尬的一段对话,学JavaSE的时候记住了重写equals方法的时候还必须要重写hashCode方法,但是却一直不知道为什么,现在补上补上。

比较的时候equals就行了,为什么还需要hashCode?

首先equals与hashcode间的关系是这样的:

  1. 如果两个对象相同(即用equals比较返回true),那么它们的hashCode值一定要相同;
  2. 如果两个对象的hashCode相同,它们并不一定相同(即用equals比较返回false) 。

重写的目的: 由于为了提高程序的效率才实现了hashCode方法,先进行hashCode的比较,如果不同,那没就不必在进行equals的比较了,这样就大大减少了equals比较的次数,这对比需要比较的数量很大的效率提高是很明显的。

举例: 假设现在在维护一个无重复的List,当我们新插入一个元素的时候,应当先判断List中是否有这个元素。假定List中存放的是Person类,如下:

1
2
3
class Person {
String name;
}

我们每次插入的时候只需要利用equals方法,依次与List中的每个Person比较,判断Person.name是否相同;但是如果Person类的属性除了name,还有age、gender、weight等特别多的属性呢?

1
2
3
4
5
6
7
8
9
class Person {
String name;
Integer age;
String gender;
float weight;
String address;
String email;
……
}

每次比较都需要比较Person类中的各个属性,这无疑会让插入的操作效率十分低下。

而重写hashCode方法,根据Person实例中的属性特征利用特定的算法计算出hash值,并将其存放到hash值所指向的地址,而后面定义进来的数据只需要看自己对应的hash值地址上是否有值,如果有值说明可能是重复的元素,需要再调用equals方法进行比较;如果没有值则直接插入,这样就极大的减少了equals方法的调用次数,插入操作执行的效率大大提升。

**示例:**重写equals方法和hashCode方法(皆是IDEA自动生成)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Person {
String name;
Integer age;
String gender;


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

Person person = (Person) o;

if (!name.equals(person.name)) {
return false;
}
if (!age.equals(person.age)) {
return false;
}
return gender.equals(person.gender);
}

@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + age.hashCode();
result = 31 * result + gender.hashCode();
return result;
}
}

Object类中的equals和hashCode方法

Object.equals()方法,比较的是对象在内存中的地址。

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

Object.hashCode()方法,返回的是对象在内存中的地址。(别问,问就是自己run一下)

1
public native int hashCode();

在源码中介绍到:

  • 每当重写equals方法时,通常都需要重写hashCode方法,以便维护hashCode方法的一般约定,即相同的对象必须具有相同的哈希代码;
  • 如果在Java应用程序的执行过程中对同一对象多次调用hashCode方法,那么hashCode方法必须始终返回相同整数,前提是没有修改对象的equals比较中使用的信息;
  • 如果两个对象根据equals(Object)方法是相等的,那么调用这两个对象中任一个对象的hashCode方法必须产生同样的整数结果;
  • 如果两个对象根据equals(Object)方法是不相等的,那么调用这两个对象中任一个对象的hashCode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不相等的对象产生截然不同的整数结果,有可能提高哈希表的性能。

最重要的是需要保证:两个对象根据equals方法是相等的,那么调用两个对象中任意的hashCode方法的值也必须相等。

总结:hashCode的引入目的是为了提高效率