重写equals()方法为什么要重写hashCode()方法


主要原因

public class Object {
  public native int hashCode();
  public boolean equals(Object obj) {
    return (this == obj);
  }
}

默认从Object继承来的hashCode是基于对象的ID实现的。如果重写了equals(基于对象的内容实现的),而保留hashCode的实现不变,那么很可能两个对象明明是“相等”,而hashCode却不一样。这样,当用其中的一个作为键保存到hashMap、hashTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到

所以如果对equals方法进行了重写,建议一定要对hashCode方法重写,以保证相同的对象返回相同的hash值,不同的对象返回不同的hash值

equals和hashCode

  • 两个对象==相等,则其hashcode一定相等,反之不一定成立
  • 两个对象equals相等,则其hashcode一定相等,反之不一定成立

在Java中,所有的对象都是继承于Object类。Ojbect类中有两个方法equalshashCode,这两个方法都是用来比较两个对象是否相等的

如果未重写equals方法,继承了object的equals方法,是比较两个对象的内存地址,显然new了2个对象内存地址肯定不一样:

public class Object {
  public boolean equals(Object obj) {
    return (this == obj);
  }
}

hashCode的通用规定:

  • 在应用程序的执行期间,只要对象的 equals方法 的比较操作所用到的信息没有被修改,那么对同一个对象的多次调用,hashCode方法 都必须始终返回同一个值在一个应用程序与另一个应用程序的执行过程中,执行 hashCode方法 所返回的值可以不一致。

    Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.

  • 如果两个对象根据 equals(Object) 方法比较是相等的,那么调用这两个对象中的 hashCode方法 都必须产生同样的整数结果。即:如果两个对象的equals()相等,那么他们的hashCode()必定相等如果两个对象的hashCode()不相等,那么他们的equals()必定不等

    If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

  • 如果两个对象根据 equals(Object) 方法比较是不相等的,那么调用这两个对象中的 hashCode方法,则不一定要求 hashCode方法 必须产生不同的结果。但是程序员应该知道,给不相等的对象产生截然不同的整数结果,有可能提高散列表的性能。

    It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

equals和hashCode在哈希容器中工作方式

在Java中,有一些哈希容器,比如Hashtable、HashMap等等,当调用这些容器的诸如get(Object obj)方法时,容器的内部肯定需要判断一下当前obj对象在容器中是否存在,然后再进行后续的操作。一般来说,判断是否存在,肯定是要将obj对象和容器中的每个元素一一进行比较,要使用equals()才是正确的。

但是如果哈希容器中的元素有很多的时候,使用equals()必然会很慢。这个时候我们想到一种替代方案就是hashCode()因为两个对象==相等,则其hashcode一定相等;两个对象equals相等,则其hashcode一定相等。至少可以先找到相同的hashCode,然后再使用equals()比较两者。如果连hashCode都不同,则这两个对象肯定不一样):

  • 当我们调用哈希容器的get(Object obj)方法时,它会首先利用查看当前容器中是否存在有相同哈希值的对象,如果不存在,那么直接返回null;
  • 如果存在,再调用当前对象的equals()方法比较一下看哈希处的对象是否和要查找的对象相同;如果不相同,那么返回null。如果相同,则返回该哈希处的对象。——如果一个类重写了equals方法但是没有重写hashCode方法,会导致,原本是相同的两个对象,可能由于没有重写hashCode方法导致在哈希容器中找不到

hashCode()返回一个int类型,两个int类型比较起来要快很多。所以说,hashCode()被设计用来使得哈希容器能高效的工作。也只有在哈希容器中,才使用hashCode()来比较对象是否相等,但要注意这种比较是一种弱的比较,还要利用equals()方法最终确认。因为两个对象equals相等,则其hashcode一定相等,反之不一定成立

如果一个类重写了equals方法但是没有重写hashCode方法,那么该类无法结合所有基于散列的集合(HashMap,HashSet)一起正常运作

String重写equals和hashcode

public final class String {
  private final char value[];

  public boolean equals(Object anObject) {
    if (this == anObject) {
      return true;
    }
    
    if (anObject instanceof String) {
      String anotherString = (String)anObject;
      int n = value.length;
      if (n == anotherString.value.length) {
        char v1[] = value;
        char v2[] = anotherString.value;
        int i = 0;
        while (n-- != 0) {
          if (v1[i] != v2[i])
            return false;
            i++;
          }
          return true;
        }
     }
     return false;
  }

  public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
      char val[] = value;

      for (int i = 0; i < value.length; i++) {
        h = 31 * h + val[i];
      }
      hash = h;
    }
    return h;
  }
}

参考资料

1.为什么要重写hashcode()方法


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM