在每个类中,在重写 equals
方法的时侯,一定要重写 hashcode
方法。如果不这样做,你的类违反了 hashCode
的通用约定, 这会阻止它在 HashMap
和 HashSet
这样的集合中正常工作。 根据 Object
规范,以下时具体约定。
- 当在一个应用程序执行过程中, 如果在
equals
方法比较中没有修改任何信息, 在一个对象上重复调用hashCode
方法时,它必须始终返回相同的值。从一个应用程序到另一个应用程序的每一次执行返回的值 可以是不一致的。 - 如果两个对象根据
equals(Object)
方法比较是相等的,那么在两个对象上调用hashCode
就必须产生的 结果是相同的整数。 - 如果两个对象根据
equals(Object)
方法比较并不相等,则不要求在每个对象上调用hashCode
都必须产生不同的结果。但是,程序员应该意识到,为不相等的对象生成不同的结果可能会提高散列表(hash tables
) 的性能。
当无法重写 hashCode
时,所违反第二个关键条款是:相等的对象必须具有相等的哈希码(hash code
)
创建一个Point
类,有两个成员变量x
和y
,并重写了equals
方法
public class Point {
private final int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Point)) return false;
Point point = (Point) obj;
return x == point.x && y == point.y;
}
}
在main方法中测试一下
public static void main(String[] args) {
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
System.out.println(p1.equals(p2));// true
Map<Point, String> map = new HashMap<>();
map.put(p1, "p1");
System.out.println(map.get(p2)); // null
}
你可能觉得 map.get(p2)
应该返回字符串 p1
, 但是却返回null
, 这是因为Point
类并没有重写hashCode
方法,导致两个相等的实例p1
和p2
返回了不同的哈希码,违反了hashCode
的约定,put
方法把实例p1
放到了一个哈希桶(hash bucket)中,但因为p2
的哈希码不等于p1
的哈希码,所以get
方法会从其它哈希桶中去查找。
解决这个方法很简单,只需要重写Point
类的hashCode
方法。
@Override
public int hashCode() {
int result = Integer.hashCode(x);
result = 31 * result + Integer.hashCode(y);
return result;
}
再次测试
public static void main(String[] args) {
Point p1 = new Point(1, 2);
Point p2 = new Point(1, 2);
System.out.println(p1.equals(p2));// true
Map<Point, String> map = new HashMap<>();
map.put(p1, "p1");
System.out.println(map.get(p2)); // p1
}
这次你会发现map.get(p2)
返回的就是字符串p1
了, 因为hashCode
这个方法会返回一个简单的确定性计算的结果,它的唯一的输入是 Point
实例中的两个重要的属性x
和y
,所以显然相等的 Point
实例具有相同的哈希码。
此外Objects
类有一个静态方法,它接受任意数量的对象并为它们返回一个哈希码。这个名为 hash
的方法可以 让你编写一行 hashCode
方法,其质量与根据这个项目中的上面编写的方法相当。
@Override
public int hashCode() {
return Objects.hash(x, y);
}
注意事项
- 当你写完
hashCode
方法后,请一定问一下自己是否满足相等的实例有相同的哈希码这一条件。 hashCode
中涉及到的属性应与equals
中保持一致,不要试图从哈希码计算中排除重要的属性来提高性能。
总之,每次重写 equals 方法时都必须重写 hashCode 方法,否则程序将无法正常运行。你的 hashCode 方 法必须遵从 Object 类指定的常规约定,并且必须执行合理的工作,将不相等的哈希码分配给不相等的实例。