问题描述
最近我通读了这个 开发者作品文档.
该文档是关于有效且正确地定义 hashCode()
和 equals()
,但是我无法弄清楚为什么我们需要覆盖这两个方法.
The document is all about defining hashCode()
and equals()
effectively and correctly, however I am not able to figure out why we need to override these two methods.
我如何决定有效地实施这些方法?
How can I take the decision to implement these methods efficiently?
推荐答案
Joshua Bloch 谈 Effective Java
Joshua Bloch says on Effective Java
您必须在每个覆盖 equals() 的类中覆盖 hashCode().不这样做将导致违反 Object.hashCode() 的一般契约,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行.
让我们通过一个例子来理解它,如果我们覆盖 equals()
而不覆盖 hashCode()
并尝试使用 Map代码>.
Let's try to understand it with an example of what would happen if we override equals()
without overriding hashCode()
and attempt to use a Map
.
假设我们有一个这样的类,并且 MyClass
的两个对象相等,如果它们的 importantField
相等(使用 hashCode()
和equals()
由eclipse生成)
Say we have a class like this and that two objects of MyClass
are equal if their importantField
is equal (with hashCode()
and equals()
generated by eclipse)
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
想象一下你有这个
Imagine you have this
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
仅覆盖equals
如果只有 equals
被覆盖,那么当你调用 myMap.put(first,someValue)
时,首先会散列到某个桶,当你调用 myMap.put(second,someOtherValue)
它将散列到另一个存储桶(因为它们有不同的 hashCode
).所以,虽然它们是相等的,但由于它们没有散列到同一个桶中,地图无法意识到它,它们都留在地图中.
If only equals
is overriden, then when you call myMap.put(first,someValue)
first will hash to some bucket and when you call myMap.put(second,someOtherValue)
it will hash to some other bucket (as they have a different hashCode
). So, although they are equal, as they don't hash to the same bucket, the map can't realize it and both of them stay in the map.
虽然如果我们重写 hashCode()
就没有必要重写 equals()
,但让我们看看在这种特殊情况下会发生什么,我们知道两个对象MyClass
如果它们的 importantField
相等,则它们相等,但我们不会覆盖 equals()
.
Although it is not necessary to override equals()
if we override hashCode()
, let's see what would happen in this particular case where we know that two objects of MyClass
are equal if their importantField
is equal but we do not override equals()
.
仅覆盖hashCode
如果你只覆盖 hashCode
那么当你调用 myMap.put(first,someValue)
时,它首先计算它的 hashCode
并存储它在给定的存储桶中.然后当你调用 myMap.put(second,someOtherValue)
时,它应该按照 地图文档 因为它们是相等的(根据业务需求).
If you only override hashCode
then when you call myMap.put(first,someValue)
it takes first, calculates its hashCode
and stores it in a given bucket. Then when you call myMap.put(second,someOtherValue)
it should replace first with second as per the Map Documentation because they are equal (according to the business requirement).
但问题是 equals 没有重新定义,所以当映射散列 second
并遍历存储桶时,查找是否有对象 k
使得 second.equals(k)
为真它不会找到任何作为 second.equals(first)
将是 false
.
But the problem is that equals was not redefined, so when the map hashes second
and iterates through the bucket looking if there is an object k
such that second.equals(k)
is true it won't find any as second.equals(first)
will be false
.
希望很清楚
这篇关于为什么我需要覆盖 Java 中的 equals 和 hashCode 方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!