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()的一般合约是:
只要在Java应用程序的执行过程中多次调用同一个对象,hashCode方法必须始终返回相同的整数,前提是在对象的equals比较中没有使用的信息被修改。 从应用程序的一次执行到同一应用程序的另一次执行,此整数不必保持一致。
如果两个对象按照equals(Object)方法相等,那么在两个对象的每一个上调用hashCode方法必须产生相同的整数结果。
如果两个对象根据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来比较项的键。