hashCode 与 equals

平时我们在项目里经常会用到 HashMap 这个数据结构,所以在面试的时候一定会被问这个问题∶你有没有重写过 hashCode 方法?你在使用 HashMap 时有没有重写 hashCode 和 equals 方法?你是怎么写的? 那么为什么要重写 hashCode 与 equals 方法,这两个方法起着什么作用呢?当我们往 HashMap 里放 key 时,首先会调用这个对象的 hashCode 方法计算它的 hash 值,随后把 key 放入 hash 值所指引的内存位置。

equals () 方法

equals 是超类 Object 中的一个基本方法,是用来判断一个对象和另一个对象是否具有相同的引用(即内存地址),如果有则返回 true,下面是 Object 类的 equals () 方法:

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

equals () 与 == 的区别

对于从 Object 继承而来的 equlas 方法,与 == 并无区别,都是比较的对象的内存地址。但是我们可以重写 equals 方法,使其按照我们的要求来进行比较。比如 String 类就重写了 equals 方法,比较的是字符串的字符序列,而不是内存地址。

equals () 的重写规则

  • 自反性:对于任何非 null 的引用值 x,x.equals (x) 应该返回 true
  • 对称性:对于任何非 null 的引用值 x 与 y,当且仅当:y.equals (x) 返回 true 时,x.equals (y) 才返回 true
  • 传递性:对于任何非 null 的引用值 x、 y 与 z,如果 y.equals (x) 返回 true,y.equals (z) 返回 true,那么 x.equals (z) 也应该返回 true
  • 一致性:对于任何非 null 的引用值 x 与 y,假设对象的 equals 比较中的信息没有被修改,则多次调用 x.equals (y) 始终返回 true 或者始终返回 false
  • 对于任何非 null 的引用值 x,x.equals (null) 应返回 false

上述这些规则在同一个类的两个对象中还是很容易理解的。

hashCode () 方法

hash code 是一种通过对象得出 Hash 值的方式,在 Java 中,每个对象都会有一个对应的 hashCode。通过算法,算出对象的 hashcode,同一个对象的 hashcode 唯一 (前提是对象没有被改变),但是不同的对象也可能有相同的 hashCode。

HashMap

HashMap 通过计算对象的 Hash 值判断对象应该在 Hash 表的哪个链上,通过 equals 方法判断是否是同一个对象。 关键是我们没有重写 hashCode 方法,调用的仍是 Object 类的 hashCode 方法(因为所有的类都是 Object 的子类),而 Object 类的 hashCode 方法返回的 hash 值其实可以看出是对象的内存地址。

因为在 hashMap 中,判断 key 是否相等首先是比较 hashCode,然后再用 equals 比较。我们重写 equals 方法是为了按我们自己的想法来比较两个对象是否相等。如果不重写 hashCode 方法,可能出现具有相同含义的不同对象(他们的 hashCode 不同)被 pass 掉的情况。而实际上他们应该是相同的 key。而如果只重写 hashCode 不重写 equals 方法,那么 equals 只是判断两个对象是否是同一个对象。所以需要同时重写 equals 和 hashCode 方法,目的是为了准确定位到我们期望的 key。