1

我正在做一些来自 Enthuware 模拟器的示例问题。这是一个示例问题

public class GoodOne
{
   int theval;
   public int hashCode()
   {
      return theval%3;
   }
   public boolean equals(Object obj)
   {
      try{
      // 1 insert code here.
      }catch(Exception e) {
        return false;
      }
   }
}

选项如下

  1. return true;
  2. return this == obj? true : (theval%3 == 0 && ((GoodOne)obj).theval%3==0) ? true :false;
  3. return theval%2 == 0? true :false;
  4. return ( (int)Math.random())*10%3 == 0? true :false;. 假设 Math.random() 返回一个介于 0.0 和 1.0 之间的双精度数(不包括 1.0)。
  5. return false;

模拟器选择的正确答案是选项2,并给出了这个解释。

这意味着如果对象theval % 3为 0,则认为对象相等。此外,如果确定两个对象相等,则它们的哈希码 ( theval % 3) 将始终相同(零)。所以它满足 hashCode 和 equals 方法的要求。

要记住的规则是:如果equals()方法返回 true, hashCode()则两个对象的 必须相同。反过来是可取的,但不是必需的。

此外,equals 方法必须遵循以下规则: 它应该是 自反的:对于任何引用值 x,x.equals(x)应该返回 true。它应该是对称的:对于任何参考值 x 和 y,x.equals(y) 当且仅当y.equals(x)返回 true 时才应该返回 true。它应该是可传递的:对于任何参考值 x、y 和 z,如果x.equals(y) 返回 true 并y.equals(z)返回 true,则x.equals(z)应该返回 true。它应该是一致的:对于任何参考值 x 和 y,x.equals(y)只要没有修改对象上的 equals 比较中使用的信息,多次调用始终返回 true 或始终返回 false。对于任何非空引用值 x,x.equals(null)应该返回假。

选项 1 是错误的,因为它将导致所有对象都被视为相等,因此hashCode()必须为所有对象返回相同的值,但事实并非如此。

选项 2 是正确的,因为hashCode()对于所有 3 的倍数都是 0。因此,如果我们在equals()方法中对所有 3 的倍数返回 true,则条件将满足。如果对象与自身进行比较,它也会返回 true。

选项 3 不正确,因为 2 和 6 将被视为相等,但它们的哈希码将不同(2 和 0)。

选项 4 是错误的,因为我们无法确定哪些对象将被视为相等。

我无法理解它的解释。有人可以详细说明为什么选项 2 在此示例中是正确的。谢谢。

4

1 回答 1

2

选项 2 是正确的,因为所有 3 的倍数的 hashCode() 都是 0。因此,如果我们在 equals() 方法中对所有 3 的倍数返回 true,则条件将满足。如果对象与自身进行比较,它也会返回 true。

好吧,我必须说,这是一个糟糕的解释,有利于为什么该选项有效。可能是因为方法本身的实现很糟糕equals()。当前,如果两个对象具有相同的哈希码(在某种程度上)hashCode(),该equals()方法返回true 。让我们再看一下那部分:

return this == obj? true : (theval%3 == 0 && ((GoodOne)obj).theval%3==0) ? true :false;

首先,该表达式可以改进为(尽管这也不是一个好的实现):

return this == obj? true : theval % 3 == ((GoodOne)obj).theval % 3;

现在theVal % 3只不过是该对象的哈希码。所以,基本上,这种方法是根据它们的哈希码来判断两个对象是否相等。(好吧,该hashCode()方法也不是良好实现的示例)。

该方法的一个大问题equals()是,它无法处理null. 我的上帝。我建议停止阅读该资源,您将其命名为什么 - Enthuware

话虽如此,这个 equals 方法可以与给定的哈希码协同工作(前提null是处理得当)。怎么样,让我们​​考虑equals方法的契约:

  • 反身:那很好。a.theVal % 3 == a.theVal % 3.
  • 对称:这也很好。对于两个对象ab,如果a.theVal % 3 == b.theVal % 3,则反之亦然。
  • 传递- 这也很好。a.theVal % 3 = b.theVal % 3并且b.theVal % 3 == c.theVal % 3, 意味着a.theVal % 3 == c.theVal % 3
  • 一致- 好吧,如果a.theVal % 3 == b.theVal % 3,那么这将永远是正确的,只要两者都没有改变a.thevalb.theVal没有改变。

equals()现在是和之间的合同hashCode()

  • 如果两个对象相等,那么它们的 hashCodes 必须相等。这在这里肯定是正确的,因为您已经根据哈希码判断对象是否相等。

所以,这解释了第二个选项在这里是如何好的。


您应该牢记以下几点:

一个非常重要的要点是,你应该使用相同的对象属性集来计算你用来判断对象是否相等的哈希码,否则合约将失效。

例如,如果您的班级中还有一个字段 - name,并且您的hashCode方法更改为:

theVal % 3 * name.hashCode();

但你没有改变equals()方法。然后,如果两个对象具有相同的theVal值,但名称不同,则根据方法实现它们将相等equals(),但鉴于上述实现,它们的哈希码将不同hashCode()


更好的 equals() 和 hashCode()?

我说过,给定的hashCode()equals()方法不是一个很好的实现。那么什么可以被认为是一个不错的实现。好吧,我建议你阅读Effective Java - Item 9,这对这个主题非常深入。

如果您使用的是 Eclipse IDE,那么您可以要求它生成这两种方法。Eclipse 生成了相当不错的实现equals()hashCode()方法。

于 2013-08-25T21:05:38.750 回答