错误的常见来源是未能覆盖 hashCode 方法。你必须
在每个覆盖equals的类中覆盖hashCode。不这样做会
导致违反 Object.hashCode 的一般合同,这将预先
让你的类与所有基于散列的集合一起正常运行
HashMap、HashSet、Hashtable等。
这是合同,从
java.lang.Object 规范:
在应用程序执行期间,只要在同一个对象上多次调用它,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。
如果两个对象根据equals(Object)方法相等,那么对这两个对象分别调用hashCode方法必须产生相同的整数结果。
如果两个对象根据equals(Object) 方法不相等,则不要求对两个对象中的每一个调用hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
未能覆盖 hashCode 时违反的关键条款是
第二个:相等的对象必须有相等的哈希码。 两个不同的
根据类的 equals 方法,实例可能在逻辑上相等,但是
Object 类的 hashCode 方法,它们只是两个对象而已
共同点。因此对象的 hashCode 方法返回两个看似随机的
数字而不是合同要求的两个相等的数字。
例如,考虑以下简单的 PhoneNumber 类,其
equals 方法是根据第 7 项中的配方构造的:
public final class PhoneNumber {
private final short areaCode;
private final short exchange;
private final short extension;
public PhoneNumber(int areaCode, int exchange,
int extension) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(exchange, 999, "exchange");
rangeCheck(extension, 9999, "extension");
this.areaCode = (short) areaCode;
this.exchange = (short) exchange;
this.extension = (short) extension;
}
private static void rangeCheck(int arg, int max,
String name) {
if (arg < 0 || arg > max)
throw new IllegalArgumentException(name +": " + arg);
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.extension == extension &&
pn.exchange == exchange &&
pn.areaCode == areaCode;
}
// No hashCode method!
... // Remainder omitted
}
假设您尝试使用此类
使用 HashMap:
Map m = new HashMap();
m.put(new PhoneNumber(408, 867, 5309), "Jenny");
此时,您可能会期望
m.get(new PhoneNumber(408 , 867,
5309)) 返回"Jenny",但它
返回null。请注意,两个 PhoneNumber 实例是
涉及:一个用于插入
进入 HashMap,然后第二个,相等,
实例用于(尝试)
恢复。 PhoneNumber 类的
未能覆盖 hashCode 原因
两个相等的实例
不相等的哈希码,违反
hashCode 合约。因此
get 方法查找电话号码
在不同的哈希桶中
一个由 put 存储的
方法。解决这个问题是
就像提供适当的 hashCode 一样简单
PhoneNumber 类的方法。
[...]