我在很多地方读到过,equals
在 Java 中覆盖方法时,也应该覆盖hashCode
方法,否则就是“违反合同”。
但是到目前为止,如果我只覆盖 equals 方法而不是 hashCode 方法,我还没有遇到任何问题。
什么是合同?为什么我在违反合同时没有遇到任何问题?如果我没有覆盖 hashCode 方法,在哪种情况下会遇到问题?
我在很多地方读到过,equals
在 Java 中覆盖方法时,也应该覆盖hashCode
方法,否则就是“违反合同”。
但是到目前为止,如果我只覆盖 equals 方法而不是 hashCode 方法,我还没有遇到任何问题。
什么是合同?为什么我在违反合同时没有遇到任何问题?如果我没有覆盖 hashCode 方法,在哪种情况下会遇到问题?
您将遇到的问题是集合中元素的唯一性是根据.equals()
和来计算.hashCode()
的,例如 a 中的键HashMap
。
顾名思义,它依赖于哈希表,而哈希桶是对象的.hashCode()
.
如果您有两个对象.equals()
,但具有不同的哈希码,您就输了!
这里重要的合同部分是:必须具有相同的对象.equals()
.hashCode()
。
这都记录在用于Object
. Joshua Bloch说你必须在Effective Java中做到这一点。说够了。
根据文档, hashCode 的默认实现将返回一些对于每个对象都不同的整数
在合理可行的情况下,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。(这通常通过将对象的内部地址转换为整数来实现,但
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 合同。
是的,它应该被覆盖。如果您认为需要覆盖equals()
,则需要覆盖hashCode()
,反之亦然。hashCode()的一般合约是:
每当在 Java 应用程序执行期间对同一个对象多次调用它时,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要从应用程序的一次执行到同一应用程序的另一次执行保持一致。
如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不需要对两个对象中的每一个调用 hashCode 方法都必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
看看Hashtables
,Hashmaps
等等HashSets
。他们都将散列密钥存储为他们的密钥。调用时get(Object key)
会生成参数的散列并在给定的散列中查找。
当不覆盖hashCode()
并且键的实例已更改时(例如根本不重要的简单字符串),hashCode()
可能会导致同一对象有 2 个不同的哈希码,从而导致在map.get()
.
约定是 if obj1.equals(obj2)
then obj1.hashCode() == obj2.hashCode()
,主要是出于性能考虑,因为maps主要使用hashCode方法来比较entry key。
契约是:如果两个对象相等,那么它们应该具有相同的哈希码,如果两个对象不相等,那么它们可能具有也可能不具有相同的哈希码。
尝试在 HashMap 中使用您的对象作为键(在 joachim-sauer 评论后编辑),您将开始面临麻烦。合同是指导方针,而不是强加于你的东西。
里面hashCode()
说:
如果根据方法两个对象相等,则对两个对象中的每一个
equals(Object)
调用方法必须产生相同的整数结果。hashCode
(我强调)。
如果您只覆盖equals()
而不是hashCode()
您的班级违反了本合同。
这在方法的JavaDoc中也是这么说的equals()
:
请注意,每当重写此方法时,通常都需要重写该
hashCode
方法,以维护该hashCode
方法的通用合同,该合同规定相等的对象必须具有相等的哈希码。