【问题标题】:Item-9: "Always override hashCode() when you override equals"第 9 项:“当你覆盖 equals 时,总是覆盖 hashCode()”
【发布时间】:2014-12-26 14:43:52
【问题描述】:

关于以下3份合同:

1) 每当在应用程序执行期间对同一个对象多次调用hashCode() 时,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。

从这句话中,我了解到,在应用程序的单次执行中,如果 hashCode() 在同一个对象上使用一次或多次,它应该返回相同的值。

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

从这个陈述中,我了解到,要在您的子类中执行相等操作(在广泛的范围内),至少有四种不同程度的相等。

(a)引用相等(==),比较两个引用类型对象的内部地址。

(b) 浅结构相等:如果两个对象的所有字段都是 ==,则它们是“相等的”。 { 例如,两个SingleLinkedList,其“大小”字段相等,其“头”字段指向相同的SListNode。}

(c) 深层结构相等:如果两个对象的所有字段都“相等”,则它们是“相等”的。 {例如,两个 SingleLinkedList 代表相同的“项目”序列(尽管 SListNodes 可能不同)。}

(d) 逻辑相等。 {两个例子: (a) 如果两个“Set”对象包含相同的元素,则它们是“相等的”,即使底层列表以不同的顺序存储元素。 (b) 分数 1/3 和 2/6 是“等于”的,即使它们的分子和分母都不同。}

基于以上四类相等,第二个合约只成立: if(Say) equals() 方法根据两个对象之间的逻辑相等返回真值 then hashCode() 方法必须也考虑在为每个新对象生成整数之前计算中的logical_equality,而不是考虑新对象的内部地址。

但是我在理解第三份合同时遇到了问题。

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

在第二份合同中,正如我们所说,hashCode() 方法应该被相应地实施[例如:在生成整数之前考虑logical_equality],我觉得,如果这样说不是真的根据equals(Object),两个对象不相等,那么hashCode() 方法可能会产生与第三个合同中提到的相同的整数结果?根据第二份合同中的论点,hashCode() 必须 产生不同的整数结果。一个只是在hashCode() 中写return 42 的人违反了第二份合同!

请帮助我理解这一点!

【问题讨论】:

  • hashCode 返回什么值类型?
  • hashCode() 返回整数
  • 你的感受并不重要。该文档指出,对于不同的值,哈希码可以相同,这很重要。这是一个与其他任何设计决策一样的设计决策 - 到目前为止,我还没有读到任何论据为什么它“必须”产生不同的值。
  • @JeroenVannevel 是的,文档说:hashcodes can be the same for distinct values,那么这意味着我们不应该在任何上下文中链接这两种方法,但我们也说,You must override hashCode() in every class that overrides equals(),因为你想从@987654344 确保什么@ 方法,无论何时覆盖 equals(),你都会覆盖 hashCode()?
  • 如果您有一组对象并且您覆盖了一个方法而不是另一个,它可能会在错误的存储桶中搜索您的对象。或者它可能会在正确的存储桶中搜索,但它可能无法识别它。更多信息:stackoverflow.com/questions/27581/…

标签: java equals


【解决方案1】:

hashCode() 不可能总是为不相等的对象返回不同的值。例如,有 2^64 个不同的 Long 值,但只有 2^32 个可能的 int 值。因此LonghashCode() 方法必须有一些重复。在这种情况下,您必须努力确保您的 hashCode() 方法尽可能均匀地分配值,并且不太可能为您在实践中最有可能使用的实例产生重复。

第二个条件只是说两个equal() 实例必须返回相同的hashCode() 值,所以这个程序必须打印true:

Long a = Long.MIN_VALUE;
Long b = Long.MIN_VALUE;
System.out.println(a.hashCode() == b.hashCode()); // a.equals(b), so must print true.

但是这个程序也打印出 true:

Long c = 0L;
Long d = 4294967297L;
System.out.println(c.hashCode() == d.hashCode()); // prints true even though !c.equals(d)

【讨论】:

  • 如果你说,hashCode() 可能不会为不相等的对象返回不同的值,那么我会问,Java 中的每个新对象是否都有不同的内部地址,所以如果你只想返回唯一的数字来自hashCode() 依赖于对象的地址。这就是 class Object hashCode() 方法的作用。
  • @overexchange 同样,不,它没有。一些实现将内部地址转换为整数。您的对象可能比 int 值更多。
  • @SotiriosDelimanolis 如果对象的数量超过 int 的最大值,那么 jvm 将什么作为地址分配给对象?
  • @overexchange 也许它使用 64 位地址,也许它使用 128 位地址。它是一个实现细节。
  • @overexchange 您不必证明这一点。它是规范的一部分。如果它的行为不像文档所说的那样,那么它就是一个错误。让开发人员处理它。您不会开始测试所有 Java 库和 JVM 细节。
【解决方案2】:

hashCode() 没有 产生不同的结果。 return 0;hashCode() 的完全合法实现 - 它确保两个 equal 对象将具有相同的哈希码。但是在使用HashMaps 和HashSets 时会确保性能不佳。

hashCode() 返回值最好是不同的(即不相等的对象应该有不同的哈希码),但这不是必需的。

【讨论】:

  • 在这里你违反了第二份合同):
  • @overexchange 不,你不是。如果它们相等,则哈希码相等。第二点没有说明如果它们不相等会发生什么。
  • @overexchangemilleniumbug 所说的 - 如果所有对象的 hashCode 为 0,那么相等的对象具有相同的 hashCode。不相等的对象也具有相同的 hashCode 这一事实与规则无关。
【解决方案3】:

第二份合同规定了当equals() 返回true 时会发生什么。当equals()返回false时,它没有说明任何情况。

第三份合同只是对这一事实的提醒。它提醒您,当equals()false 对于两个对象时,它们的哈希码之间没有联系。它们可能相同也可能不同,具体取决于实现方式。

【讨论】:

  • Arkady,我在说什么,我想知道,为什么合同#2 错过了说当equals() 返回false 时hashCode() 必须返回不同的整数?因为当您覆盖equals() 方法时,您将遵循查询(a/b/c/d)中提到的四个级别的相等操作中的任何一个,因此hashCode() 方法也应该相应地遵循该逻辑。
  • 仔细阅读#2。它完全没有说明当 equals() 为假时会发生什么。它只讨论“如果两个对象根据 equals(Object) 方法相等”。没有讨论在相反情况下会发生什么。
  • 要了解 why #2 未指定 != 大小写,请阅读其余答案。基本上,int 不足以确保每个可能类型的每个可能值都有一个不同的数字。因此,Java 架构师通过不指定 != 大小写来避免不可能完成的任务。
  • 好的。所以int 不是那么大并且对象的数量超过“int.max”那么我可以清楚地说hashCode() 在这种情况下必须依赖long。如果你尝试这样做,java 是否允许你说(覆盖)public long hashCode(){} 那么你想如何解决这个问题?首先,两个新对象如何在超过 2^31-1 的情况下具有相同的地址,它们必须不同?我能想到的唯一可能的答案是java必须说可以创建的对象数量的限制。
  • 你在想“内存中当前分配的所有对象”。您应该考虑的是“给定类型的所有可能对象”。考虑字符串。有多少个可能的长度为 1M 的字符串?如何确保每两个这样的字符串都有不同的哈希码?
【解决方案4】:

第三点意味着你可以有许多具有相同哈希码的不相等对象。 .例如 2 个字符串对象可以具有相同的哈希码。第二点指出两个相等的对象必须具有相同的哈希码。 . return 5 是一个有效的哈希实现,因为它为 2 个相等的对象返回相同的值。

【讨论】:

  • hashCode() of class String 的工作方式如下:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]equals() of class String 的工作方式如下:true if same sequence of characters as this object 所以我们遵循“深度结构平等”的合同#2在这两种方法中。那么考虑合同#3的机会在哪里呢? joshua 说:The key provision that is violated when you fail to override hashCode is the second(contract) one 是什么意思
  • @ over exchange : s1= new String ("abc") 和 s2= new String("abc") 的情况如何?它们是具有相同哈希码的 2 个不同对象。约书亚试图说 - “第二个合约必须对对象保持良好......并且它保持良好的方式是通过覆盖哈希码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-02
  • 2011-04-24
  • 2020-06-09
  • 2017-11-19
  • 1970-01-01
相关资源
最近更新 更多