转载

为啥子重写equals方法时一定要重写hashCode方法

在每个类中,在重写 equals 方法的时侯,一定要重写 hashcode 方法。如果不这样做,你的类违反了 hashCode 的通用约定, 这会阻止它在 HashMap 和 HashSet 这样的集合中正常工作。 根据 Object 规范,以下时具体约定。

  1. 当在一个应用程序执行过程中, 如果在 equals 方法比较中没有修改任何信息, 在一个对象上重复调用 hashCode 方法时,它必须始终返回相同的值。从一个应用程序到另一个应用程序的每一次执行返回的值 可以是不一致的。
  2. 如果两个对象根据 equals(Object) 方法比较是相等的,那么在两个对象上调用 hashCode 就必须产生的 结果是相同的整数。
  3. 如果两个对象根据 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 类指定的常规约定,并且必须执行合理的工作,将不相等的哈希码分配给不相等的实例。

原文  https://juejin.im/post/5e0226bb6fb9a0165936f44b
正文到此结束
Loading...