转载

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

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

  1. 当在一个应用程序执行过程中, 如果在 equals 方法比较中没有修改任何信息, 在一个对象上重复调用 hashCode 方法时,它必须始终返回相同的值。从一个应用程序到另一个应用程序的每一次执行返回的值 可以是不一致的。
  2. 如果两个对象根据 equals(Object) 方法比较是相等的,那么在两个对象上调用 hashCode 就必须产生的 结果是相同的整数。
  3. 如果两个对象根据 equals(Object) 方法比较并不相等,则不要求在每个对象上调用 hashCode 都必须产生不同的结果。但是,程序员应该意识到,为不相等的对象生成不同的结果可能会提高散列表( hash tables ) 的性能。

当无法重写 hashCode 时,所违反第二个关键条款是:相等的对象必须具有相等的哈希码( hash code

创建一个 Point 类,有两个成员变量 xy ,并重写了 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 方法,导致两个相等的实例 p1p2 返回了不同的 哈希码 ,违反了 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 实例中的两个重要的属性 xy ,所以显然相等的 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...