Joshua Bloch 在《Effective Java》中说道
您必须在每个重写 equals() 的类中重写 hashCode()。如果不这样做,将导致违反 Object.hashCode() 的一般契约,这将阻止您的类与所有基于哈希的集合(包括 HashMap、HashSet 和 Hashtable)一起正常运行。
让我们尝试通过一个例子来理解它,如果我们重写会发生什么equals()
不覆盖hashCode()
并尝试使用Map
.
假设我们有一个这样的类和两个对象MyClass
相等,如果他们的importantField
是相等的(与hashCode()
and equals()
由 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;
}
}
想象一下你有这个
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
仅覆盖equals
If only equals
被覆盖,那么当你打电话时myMap.put(first,someValue)
首先将散列到某个存储桶,当您调用时myMap.put(second,someOtherValue)
它将散列到其他一些存储桶(因为它们有不同的hashCode
)。因此,尽管它们相等,但由于它们不散列到同一个存储桶,因此映射无法实现这一点,并且它们都保留在映射中。
虽然没有必要重写equals()
如果我们重写hashCode()
,让我们看看在这种特殊情况下会发生什么,我们知道两个对象MyClass
相等,如果他们的importantField
是相等的,但我们不覆盖equals()
.
仅覆盖hashCode
如果你只覆盖hashCode
然后当你打电话时myMap.put(first,someValue)
它首先需要计算它的hashCode
并将其存储在给定的桶中。然后当你打电话时myMap.put(second,someOtherValue)
它应该根据以下内容将第一个替换为第二个地图文档因为它们是平等的(根据业务要求)。
但问题是equals没有被重新定义,所以当map散列时second
并遍历桶查找是否有对象k
这样second.equals(k)
是真的,它不会找到任何作为second.equals(first)
将false
.
希望已经清楚了