最近我通读了这篇文章
该文件旨在有效且正确地定义hashCode()
和equals()
,但我无法理解为什么我们需要覆盖这两种方法.
我如何才能做出有效实施这些方法的决定呢?
最近我通读了这篇文章
该文件旨在有效且正确地定义hashCode()
和equals()
,但我无法理解为什么我们需要覆盖这两种方法.
我如何才能做出有效实施这些方法的决定呢?
约书亚·布洛赫说有效的Java
必须在每个重写equals()的类中重写hashCode().否则将导致违反标的物总合同.hashCode(),这将阻止类与所有基于哈希的集合(包括HashMap、HashSet和Hashtable)一起正常运行.
让我们通过一个例子来try 理解它,如果我们在不覆盖hashCode()
的情况下覆盖equals()
,并try 使用Map
.
假设我们有这样一个类,如果两个MyClass
的对象的importantField
相等(其中hashCode()
和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");
Override only 100
如果只覆盖了equals
,那么当您首先调用myMap.put(first,someValue)
时,它将散列到某个bucket,当您调用myMap.put(second,someOtherValue)
时,它将散列到另一个bucket(因为它们有不同的hashCode
).因此,虽然它们是相等的,因为它们不会散列到同一个桶中,但 map 无法实现这一点,它们都留在 map 中.
虽然如果我们覆盖hashCode()
,就没有必要覆盖equals()
,但让我们看看在这个特殊情况下会发生什么,我们知道MyClass
的两个对象是相等的,如果它们的importantField
相等,但我们不覆盖equals()
.
Override only 100
如果您只覆盖hashCode
,那么当您调用myMap.put(first,someValue)
时,它会首先计算hashCode
并将其存储在给定的存储桶中.然后,当您调用myMap.put(second,someOtherValue)
时,它应该按照Map Documentation将第一个替换为第二个,因为它们是相等的(根据业务需求).
但问题是equals没有被重新定义,所以当map散列second
并在bucket中迭代时,如果有一个对象k
,那么second.equals(k)
是真的,那么它将找不到任何对象,因为second.equals(first)
将是false
.
希望是清楚的