于,Point 的上次实现违法了Object 类中定义的hashCode约定:
如果两个对象根据equals(Object) 方法是等值的,那么对两个对象中任何一个调用 hashCode 方法都必须得到相同的整型结果。
事实上,在Java中,通常应同时对 hashCode 和equals进行重定义,这一事实是广为人知的。此外,hashCode 可能仅依赖equals所依赖的域。对于 Point 类,以下将是一个合适的 hashCode 定义:
public class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
@Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof Point) {
Point that = (Point) other;
result = (this.getX() == that.getX() && this.getY() == that.getY());
}
return result;
}
@Override public int hashCode() {
return (41 * (41 + getX()) + getY());
}
}
这只是 hashCode 多种可能的实现中的一种。将常量 41 加到一个整型域 x 上,所得结果再乘以素数 41,然后在加上另一个整型域 y。这样就可以提供合理分布的散列码,而运行时间和代码大小也会降低。
在定义与 Point 相似的类时,添加 hashCode 解决了等值的问题。但是,还有其他的问题需要注意。
Java:所有的equals方法实现都是错误的?(4)
时间:2011-01-05 司马牵牛译
陷阱 3 :对equals进行定义时涉及可变域
以下对 Point 类进行一项细微的修改:
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
@Override public boolean equals(Object other) {
boolean result = false;
if (other instanceof Point) {
Point that = (Point) other;
result = (this.getX() == that.getX() && this.getY() == that.getY());
}
return result;
}
@Override public int hashCode() {
return (41 * (41 + getX()) + getY());
}
}
唯一的不同之处是域 x 和 y 不再是 final 类型,同时添加了两个集合方法,允许用户更改 x 和 y 值。现在,equals和 hashCode 方法的定义涉及了这些可变域,因此域更改时它们的结果也将改变。一旦你将点放入集合中,这会带来很奇怪的效果:
Point p = new Point(1, 2);
HashSet< Point> coll = new HashSet< Point>();
coll.add(p);
System.out.println(coll.contains(p)); // 输出 true
现在,如果更改点 p 中的域,集合还将包含该点吗? 我们来试试下面的代码:
p.setX(p.getX() + 1);
System.out.println(coll.contains(p)); // (很可能)输出 false
这看起来很奇怪。p 到哪里去了?如果你对集合的 iterator 是否包含 p 进行测试,会得到各位奇怪的结果:
Iterator< Point> it = coll.iterator();
boolean containedP = false;
while (it.hasNext()) {
Point nextP = it.next();
if (nextP.equals(p)) {
containedP = true;
break;
}
}
System.out.println(containedP);
|