如果我不想将我的对象用作散列 table 中的键,为什么两个相等的对象 return 应该等于散列码?
Why should two equal objects return equal hash codes if I don't want to use my object as a key in a hash table?
Object.hashCode() 的 Java 文档是这样说的:
If two objects are equal according to the equals(Object) method, then
calling the hashCode method on each of the two objects must produce
the same integer result.
但他们没有解释为什么 两个相等的对象必须return 相等的哈希码。为什么 Oracle 工程师决定在覆盖 equals
时必须覆盖 hashCode
?
equals
的典型实现不调用 hashCode
方法:
@Override
public boolean equals(Object arg0) {
if (this == arg0) {
return true;
}
if (!(arg0 instanceof MyClass)) {
return false;
}
MyClass another = (MyClass) arg0;
// compare significant fields here
}
在 Effective Java(第 2 版) 我读到:
Item 9: Always override hashCode when you override equals.
A common source of bugs is the failure to override the hashCode
method. You must override hashCode in every class that overrides
equals. Failure to do so will result in a violation of the general
contract for Object.hashCode, which will prevent your class from
functioning properly in conjunction with all hash-based collections,
including HashMap, HashSet, and Hashtable.
假设我不需要使用 MyClass
作为散列 table 的键。为什么我需要在这种情况下覆盖 hashCode()
?
常见的智慧表明 equals()
和 compareTo()
都需要 // compare significant fields here
的逻辑(如果您想对 MyClass
的实例进行排序)和也用于使用哈希表。因此将此逻辑放在 hashCode()
中并让其他方法使用哈希码是有意义的。
如文中所说,违反了正在使用的总合同。当然,如果你从来没有在任何需要 hashCode 的地方使用它,没有人会强迫你实现它。
但是如果将来某天需要它怎么办?如果 class 被其他开发人员使用怎么办?这就是为什么有这个合同,他们两个都必须执行,所以不会混淆。
显然,如果没有人调用您的 class 的 hashCode
方法,就没有人会知道它与 equals
不一致。你能保证在你的项目期间,包括维护期间,没有人需要,比如说,从列表中删除重复的对象或将一些额外的数据与你的对象相关联吗?
您可能只是更安全地实施 hashCode
,以便与 equals 保持一致。这不是特别难,总是 returning 0 已经是一个有效的实现。
(请不要只是 return 0)
当然,当你有一个你自己写的小程序并且你每次使用外部库时都检查它不依赖于 hashCode()
然后你可以忽略所有这些警告。但是,当软件项目增长时,您将使用外部库,而这些库将依赖 hashCode()
,您将浪费大量时间寻找错误。或者在较新的 Java 版本中,其他 类 也使用 hashCode()
并且您的程序将失败。
因此,实现它并遵循这个简单的规则要容易得多,因为现代 IDE 可以通过单击自动生成 equals
和 hashCode
。
更新一个小故事:在工作中,我们在很多类中也忽略了这条规则,只实现了最需要的那个equals
或compareTo
。有一天会发生一些奇怪的事情,因为一位程序员在 GUI 中使用了 Hash*-Class 而我们的对象没有遵循这个规则。最后一个学徒需要用equals
搜索所有类并且必须添加相应的hashCode
方法。
必须覆盖 hashCode
以同意 equals
的原因是因为 hashCode
的使用原因和方式。哈希码用作值的代理项,因此在将键值映射到某些东西时,可以使用哈希来提供近乎恒定的查找时间和合理的 space。当两个值比较相等(即,它们是相同的值)时,当用作散列集合的键时,它们必须映射到相同的东西。这要求它们具有相同的哈希码才能到达那里。
您必须适当地覆盖 hashCode
,因为手册上说您必须这样做。它说您必须这样做,因为做出的决定是图书馆可以假设您满足该合同,因此他们(和您)在使用您提供的值作为其函数实现中的键时可以获得散列的性能优势。
Object.hashCode() 的 Java 文档是这样说的:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
但他们没有解释为什么 两个相等的对象必须return 相等的哈希码。为什么 Oracle 工程师决定在覆盖 equals
时必须覆盖 hashCode
?
equals
的典型实现不调用 hashCode
方法:
@Override
public boolean equals(Object arg0) {
if (this == arg0) {
return true;
}
if (!(arg0 instanceof MyClass)) {
return false;
}
MyClass another = (MyClass) arg0;
// compare significant fields here
}
在 Effective Java(第 2 版) 我读到:
Item 9: Always override hashCode when you override equals.
A common source of bugs is the failure to override the hashCode method. You must override hashCode in every class that overrides equals. Failure to do so will result in a violation of the general contract for Object.hashCode, which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.
假设我不需要使用 MyClass
作为散列 table 的键。为什么我需要在这种情况下覆盖 hashCode()
?
常见的智慧表明 equals()
和 compareTo()
都需要 // compare significant fields here
的逻辑(如果您想对 MyClass
的实例进行排序)和也用于使用哈希表。因此将此逻辑放在 hashCode()
中并让其他方法使用哈希码是有意义的。
如文中所说,违反了正在使用的总合同。当然,如果你从来没有在任何需要 hashCode 的地方使用它,没有人会强迫你实现它。
但是如果将来某天需要它怎么办?如果 class 被其他开发人员使用怎么办?这就是为什么有这个合同,他们两个都必须执行,所以不会混淆。
显然,如果没有人调用您的 class 的 hashCode
方法,就没有人会知道它与 equals
不一致。你能保证在你的项目期间,包括维护期间,没有人需要,比如说,从列表中删除重复的对象或将一些额外的数据与你的对象相关联吗?
您可能只是更安全地实施 hashCode
,以便与 equals 保持一致。这不是特别难,总是 returning 0 已经是一个有效的实现。
(请不要只是 return 0)
当然,当你有一个你自己写的小程序并且你每次使用外部库时都检查它不依赖于 hashCode()
然后你可以忽略所有这些警告。但是,当软件项目增长时,您将使用外部库,而这些库将依赖 hashCode()
,您将浪费大量时间寻找错误。或者在较新的 Java 版本中,其他 类 也使用 hashCode()
并且您的程序将失败。
因此,实现它并遵循这个简单的规则要容易得多,因为现代 IDE 可以通过单击自动生成 equals
和 hashCode
。
更新一个小故事:在工作中,我们在很多类中也忽略了这条规则,只实现了最需要的那个equals
或compareTo
。有一天会发生一些奇怪的事情,因为一位程序员在 GUI 中使用了 Hash*-Class 而我们的对象没有遵循这个规则。最后一个学徒需要用equals
搜索所有类并且必须添加相应的hashCode
方法。
必须覆盖 hashCode
以同意 equals
的原因是因为 hashCode
的使用原因和方式。哈希码用作值的代理项,因此在将键值映射到某些东西时,可以使用哈希来提供近乎恒定的查找时间和合理的 space。当两个值比较相等(即,它们是相同的值)时,当用作散列集合的键时,它们必须映射到相同的东西。这要求它们具有相同的哈希码才能到达那里。
您必须适当地覆盖 hashCode
,因为手册上说您必须这样做。它说您必须这样做,因为做出的决定是图书馆可以假设您满足该合同,因此他们(和您)在使用您提供的值作为其函数实现中的键时可以获得散列的性能优势。