重寫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