equals() and hashCode() in Java

equals()

1
2
3
4
// defined in Object class
public boolean equals(Object obj) {
return (this == obj);
}

equals()的Override, 应该遵循以下原则:

  • 自反性:x.equals(x) return true

  • 对称性:y.equals(x) return true <=> x.equals(y) return true

  • 传递性:x.equals(y) return true, y.equals(z) return true => x.equals(z) return true

  • 一致性:无论对**x.equals(y)**调用多少次,结果都应该一致; 除非equals()中使用到的属性被更改了

  • 非空性:任何非空的引用值X,x.equals(null)的返回值一定为false

hashCode()

1
2
3
// defined in Object class
@HotSpotIntrinsicCandidate
public native int hashCode();
  • 在某个Java程序的一次执行过程中, 只要equals()使用的属性没有被改变, 那么同一个对象无论调用多少次hashCode(), 其返回的值都必须一致
  • 在同一个Java程序的多次执行过程中, 一个对象的hashCode()返回值,不必保持相同

Override Rule

为什么重写equals()的时候,必须重写hashCode()?

  • Java的general contract规定, 相等的对象,必须具有相同的hash code
  • Object对象默认实现的hashCode(), 是为每一个对象返回不同的值

equals()和hashCode()的Override, 应该遵循以下原则:

  • o1.equals(o2)为true, 则o1.hashCode() == o2.hashCode()为true

  • 反之, 不成立

  • equals不相等的两个对象, hashCode可能相等

  • However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

1
2
3
o1.equals(o2)
==>
o1.hashCode() == o2.hashCode()

Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class User {
private int id;
private String name;
private String email;

@Override
public int hashCode() {
return Objects.hashCode(id, name, email);
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User user)) {
return false;
}
return id == user.id &&
Objects.equal(name, user.name) &&
Objects.equal(email, user.email);
}
}

Another solution: hashCode()

1
2
3
4
5
6
7
@Override
public int hashCode() {
int result = id;
result = 31 * result + name.hashCode();
result = 31 * result + email.hashCode();
return result;
}

Objects.hashCode()

1
2
3
public static int hashCode(@Nullable Object @Nullable ... objects) {
return Arrays.hashCode(objects);
}

Arrays.hashCode()

1
2
3
4
5
6
7
8
9
10
11
public static int hashCode(Object a[]) {
if (a == null)
return 0;

int result = 1;

for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());

return result;
}

Why is the multiplier 31?

  • 选择质数, 减少哈希碰撞(Hash Collision)
  • 不选择较小的质数, e.g. 2; 因为冲突率依然很高
  • 不选择较大的质数, e.g. 101; 会产生溢出
  • 31 * i 可被jvm优化为(i << 5) - i