Xudong's Blog

Java中的equals和hashCode方法

Word count: 901Reading time: 3 min
2018/10/04

equals()

java中处处是对象。所有类都继承自Object基类,Object基类拥有一个equals()方法。所以无论是继承自Object,还是override了基类的,所有类都拥有equals()方法。

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

上面是Object类的equals()方法,很简单,使用了 == 比较了两个对象。
而 == 是java中的运算符,用在两个对象的引用之间时,作用是比较两个对象的地址。
所以那个类如果没有override这个方法,在其对象上使用equals()和==是等效的。

1
2
3
4
5
6
7
8
9
10
11
public void compareObject() {
StringBuffer strOne = new StringBuffer("String");
StringBuffer strTwo = new StringBuffer("String");
System.out.println(strOne==strTwo);
System.out.println(strOne.equals(strTwo));

Integer intOne = new Integer(999);
Integer intTwo = new Integer(999);
System.out.println(intOne==intTwo);
System.out.println(intOne.equals(intTwo));
}
1
2
3
4
false
false
false
true

StringBuffer也是一种字符串类,需要注意的是它没有override基类的equals方法。所以即使对象中的内容一样,equals()返回的也是false。
而Integer类实现了它自己的equals()方法,内容同样是999的两个对象被equals()正确判断为true。

下面是Integer类实现的equals()

1
2
3
4
5
6
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}

如果我们自己写的类也需要进行比较,那么我们实现的equals()需要比较我们类对象中的内容,这样才符合java规范。

hashCode()

接下来说一下这两个方法的关系。

如果一个类需要重写equals()方法,那么同时也应该重写hashCode()方法。因为java文档规定了,如果A.equals(B)返回为true,那么A.hashCode()应当等于B.hashCode()。

Effective Java 3rd edition
Item 11 : Always override hashCode when you override equals

为什么要这样呢?首先从HashMap的源码我们知道,当从map中找一个指定的key时,我们首先是根据这个key的hashCode()的返回值经过处理后生成的hash值找到对应的桶,再遍历这个桶找到相等的key。

从以上过程可以提炼出非常重要的一点,相等的对象hashCode()返回值一定要相等,因为如果hashCode()返回值不相等的话,计算出来的hash值很可能是不相等的,这会直接导致相同的key可能放入不同的桶中,这是不被允许的。所以如果我们要用自定义类对象做散列表的key时,在重写了equals()后,还要重写hashCode()来保证equals()返回为true的情况下两个对象hashCode()的返回值也相等。

测试

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
35
public class Test {
public static void main(String[] args) {
HashMap<People, Integer> map = new HashMap<>();
// 生成两个相等的对象
People p1 = new People(11, "Jack");
People p2 = new People(11, "Jack");

map.put(p1, 1);
// 当没重写hashCode()时输出的是null
// 重写时输出的是 1
System.out.println(map.get(p2)); // OUTPUT: 1
}
}

class People {
int age;
String name;

public People(int age, String name) {
this.age = age;
this.name = name;
}

// 我们认为只要两个People对象age和name相等则这两个对象相等
@Override
public boolean equals(Object obj) {
return ((People) obj).age == age && ((People) obj).name.equals(name);
}

// equals()返回为true时,两个对象的hashCode()返回值也是一样的
@Override
public int hashCode() {
return age * name.hashCode();
}
}

总结

为了避免在使用散列容器出现错误,当我们重写自定义类的equals()方法时,还要重写hashCode()方法,以保证相等的对象hashCode()返回值必定相等。

如果忽略了equals()以及hashCode()方法,其他程序员或者其他工具类按照标准去使用你的定制类就会出现错误。

CATALOG
  1. 1. equals()
  2. 2. hashCode()
    1. 2.1. 测试
  3. 3. 总结