哈希码和排除字段

Hashcode and excluding fields

根据 Bloch 关于 hashCode 的建议:

You may ignore any field whose value can be computed from fields included in the computation

我不明白这个。有人可以举一个真实的例子吗?我假设任何可以从其他字段生成字段的 class 出于性能原因只会将其存储在另一个变量中。但这与哈希码合约有什么关系?

class Example {
  final int a;
  final int b;
  final int c;

  Example(int a, int b) { this.a = a; this.b = b; this.c = a + b; }
}

在此处的 hashCode 计算中包含 c 没有任何意义:ab 相等的任何实例也将具有 c s 相等。

Objects.hash(this.a, this.b, this.c) == Objects.hash(that.a, that.b, that.c)
  <=>
Objects.hash(this.a, this.b) == Objects.hash(that.a, that.b)

因此,您所做的只是 "perturbing" 通过包含 c 哈希码,即使其成为不同的值,但不是以有意义的方式。


一个实际的例子在String中:String有一个字段,hash,存储哈希码的值,避免重复计算(source):

public int hashCode() {
    int h = hash;  // <-- hash is a member variable.
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

也许很明显,这不能算在哈希码计算中!但是可以从class.

中的其他字段导出

考虑一个 class 你有更多字段

class MyClass {
   String name;
   String surname;
   String fullName;
}

在这个class中,对于哈希码的生成,唯一有意义的字段是namesurname。其余字段fullName由其他人生成,可以忽略。

But what does this have to do with the hashcode contract?

它不属于 hashcode() 合同的一部分。您应该将其视为以有效方式实施它的良好做法(因为它省去了一些无用的计算)。

事实上,hashCode() 的良好实现应该依赖于与 equals() 方法使用的字段相同的字段。
如果您有一个包含 3 个字段的 class,例如 Addition :

class Addition {
  private float operandOne;
  private float operandTwo;
  private float result;
}

您不需要在 equals() 方法中使用 result 因为 result 在某种程度上是冗余信息,您不需要它来识别Addition 实例的独特方式。
您知道如果一个 Addition 实例中的 operandOneoperandTwo 与另一个 Addition 实例中的 operandOneoperandTwo 匹配,就足够了认为他们是平等的。

hashcode()方法中也是一样。