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

我读了很多地方说,而覆盖equals Java中的方法,也应该重写hashCode方法,否则是“违反合同”。

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

什么是合同? 而我违反合同的时候为什么不面临什么问题呢? 在这种情况下,如果我没有重写hashCode方法,我会面临一个问题吗?

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

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

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

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

这全部logging在Object的javadoc中 。 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()的一般合约是:

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

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

  3. 如果两个对象根据equals(java.lang.Object)方法不相等,则不要求对两个对象中的每个对象调用hashCode方法都必须产生不同的整数结果。 但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

请参阅java.lang.Object JavaDoc

hashCode()说:

如果两个对象按照equals(Object)方法equals(Object) ,那么在两个对象的每一个上调用hashCode方法必须产生相同的整数结果

(强调我)。

如果你只覆盖equals()而不是hashCode()你的类违反了这个合同。

这也在equals()方法的JavaDoc中说过:

请注意,无论何时重写此方法,通常都必须重写hashCode方法,以维护hashCode方法的一般约定,该方法声明相等的对象必须具有相同的哈希码。

看看Hashtables,Hashmaps,HashSets等等。 它们都将散列键存储为键。当调用get(Object key)时,会生成参数的散列值,并在给定散列值中查找。

当不覆盖hashCode()并且键的实例已经被改变(例如一个简单的string根本就不重要)时,hashCode()会导致同一对象有两个不同的哈希码,导致找不到给定map.get()中的键。

合同是:如果两个对象相等,那么他们应该有相同的哈希码,如果两个对象不相等,那么他们可能会或可能不会有相同的哈希码。

尝试在HashMap中使用你的对象作为键(在joachim-sauer的评论后编辑),你将开始面临麻烦。 合同是一个准则,而不是强加给你的东西。

合同是,如果obj1.equals(obj2)然后obj1.hasCode() == obj2.hashCode() ,它主要是出于性能的原因,因为地图主要是使用hashCode来比较项的键。