Java 中 hashCode 和 equals 方法之间的关系

我在很多地方读到说,在 Java 中覆盖 equals方法时,也应该覆盖 hashCode方法,否则就是“违反合同”。

但是到目前为止,如果只重写 equals 方法,而不重写 hashCode 方法,我还没有遇到任何问题。

合同是什么?为什么我在违反合同的时候没有遇到任何问题?在哪种情况下,如果我没有覆盖 hashCode 方法,我将面临一个问题?

172974 次浏览

一个约定是: 如果两个对象是相等的,那么它们应该有相同的哈希代码; 如果两个对象不相等,那么它们可能有相同的哈希代码,也可能没有相同的哈希代码。

Try using your object as key in HashMap (edited after comment from joachim-sauer), and you will start facing trouble. A contract is a guideline, not something forced upon you.

您将遇到的问题是集合中元素的统一性是根据 .equals().hashCode()计算的,例如 HashMap中的键。

顾名思义,它依赖于散列表,而散列桶是对象的 .hashCode()函数。

如果你有两个对象是 .equals(),但有不同的散列码,你输了!

这里合同中最重要的部分是: 对象的 ABC0必须具有相同的 .hashCode()

这些都在 Object的 javadoc中有记录。而 约书亚 · 布洛赫说你必须在 Effective Java中做。说得够多了。

是的,它应该被覆盖。如果您认为需要重写 equals(),那么需要重写 hashCode(),反之亦然。HashCode ()的总合同是:

  1. 只要在 Java 应用程序的执行过程中对同一对象调用多次,hashCode 方法就必须始终返回相同的整数,前提是不修改对象的等于比较中使用的任何信息。从应用程序的一次执行到同一应用程序的另一次执行,这个整数不需要保持一致。

  2. 如果根据 equals (Object)方法,两个对象是相等的,那么对每个对象调用 hashCode 方法必须产生相同的整数结果。

  3. It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hashtables.

契约是,如果 obj1.equals(obj2)然后 obj1.hashCode() == obj2.hashCode(),它主要是出于性能的原因,因为地图主要是使用 hashCode 方法来比较条目键。

java.lang.Object的 JavaDoc

hashCode()中写道:

If two objects are equal according to the equals(Object) method, 然后对两个对象中的每一个 < strong > must 调用 hashCode方法 产生相同的整数结果 .

(我强调)。

If you only override equals() and not hashCode() your class violates this contract.

equals()方法的 JavaDoc 中也是这样说的:

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general hashCode方法的合同,该方法声明相等的对象必须 有相同的哈希码。

看看 HashtablesHashmapsHashSets等等。他们都将哈希键存储为自己的钥匙。当调用 get(Object key)时,将生成参数的散列并在给定的散列中进行查找。

当没有覆盖 hashCode()并且键的实例已经被改变(例如一个简单的字符串,根本不重要)时,hashCode()可能会导致同一个对象的两个不同的散列码,导致在 map.get()中找不到给定的键。

根据 doc,hashCode 的默认实现将返回一些对每个对象都不同的整数

由 Object 类定义的 hashCode 方法尽可能实用 return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation
JavaTM 编程语言并不需要这种技术。)

但是,有时您希望具有相同含义的不同对象的哈希代码是相同的。比如说

Student s1 = new Student("John", 18);
Student s2 = new Student("John", 18);
s1.hashCode() != s2.hashCode(); // With the default implementation of hashCode

如果在集合框架(如 HashTable、 HashSet)中使用哈希数据结构,就会出现这种问题。特别是像 HashSet 这样的集合,你最终会有重复的元素并且违反 Set 协议。