1. 程式人生 > 其它 >Java 中 equals() 和 hashcode() 方法詳解

Java 中 equals() 和 hashcode() 方法詳解

hashCode 使用中產生的問題

HashSet 是一個無序、不可重複的集合,程式碼如下:

public class HashSetDemo {
    public static void main(String[] args) {
        Set<Student> sets = new HashSet<>();
        sets.add(new Student(1,"張三",23));
        sets.add(new Student(2,"李四",24));
        sets.add(new Student(3,"王五",22));
        sets.add(new Student(4,"趙六",27));
        sets.add(new Student(4,"趙六",27));

        for (Student student: sets) {
            System.out.println(student);
        }
    }
}

執行列印結果如下:

Student{id=4, name='趙六', age=27, clazz='null', score=0.0}
Student{id=3, name='王五', age=22, clazz='null', score=0.0}
Student{id=4, name='趙六', age=27, clazz='null', score=0.0}
Student{id=1, name='張三', age=23, clazz='null', score=0.0}
Student{id=2, name='李四', age=24, clazz='null', score=0.0}

是不是結果和我們想象的不一樣,HashSet 集合元素不是不重複,為何這裡卻發生的重複現象呀?這其實是因為 equals、hashCode 使用不規範導致的。那麼,equals 和 hashCode 到底有何關係呢?為何影響 HashSet 的使用?

equals 與 Hashcode 的關係

它們都是 Object 類中的方法,如下:

public boolean equals(Object obj)
public int hashCode()
  • equals(): 用來判斷兩個物件是否相同,在 Object 類中是通過判斷物件間的記憶體地址來決定是否相同
  • hashCode(): 獲取雜湊碼,也稱為雜湊碼,返回一個int整數。這個雜湊碼的作用是確定該物件在雜湊表中的索引位置。

閱讀 hashCode 方法的註釋如下:

  1. hashCode() 方法返回物件的雜湊碼,支援該方法是為雜湊表提供一些優點,例如,HashMap 提供的雜湊表。
  2. 同一個物件未發生改變時多次呼叫 hashCode() 返回值必須相同
  3. 兩個物件 equals 不相等,那麼兩物件的 hashCode() 返回必定不同(此處可用來提高雜湊表效能)
  4. 兩個物件的 hashCode() 返回值相同,兩物件不一定相同,還需要通過 equals() 再次判斷
  5. 當 equals 方法被重寫時,通常有必要重寫 hashCode 方法

通過第 1 點其實可以看出,hashCode() 在散列表中才有用,在其它情況下沒用。在散列表中 hashCode() 的作用是獲取物件的雜湊碼,進而確定該物件在散列表中的位置,當物件不會用來建立像 hashMap、hashSet 等散列表時,hashCode() 實際上用不上。

產生問題的原因

分析原因前需要了解雜湊表的底層實現,hashCode 在雜湊表中充當的作用:

  • 假設記憶體中有0 1 2 3 4 5 6 7 8這8個位置,如果我有個欄位叫做 ID,那麼我要把這個欄位存放在以上8個位置之一,如果不用HashCode而任意存放,那麼當查詢時就需要到8個位置中去挨個查詢
  • 使用 HashCode 則效率會快很多,把ID的 HashCode%8,然後把 ID 存放在取得餘數的那個位置,然後每次查詢該類的時候都可以通過ID的 HashCode%8 求餘數直接找到存放的位置了
  • 如果 ID 的HashCode%8 算出來的位置上本身已經有資料了怎麼辦?這就取決於演算法的實現了,比如 ThreadLocal 中的做法就是從算出來的位置向後查詢第一個為空的位置,放置資料;HashMap 的做法就是通過鏈式結構連起來。反正,只要保證放的時候和取的時候的演算法一致就行了。
  • 如果ID的 HashCode%8 相等怎麼辦(這種對應的是第三點說的鏈式結構的場景)?這時候就需要定義 equals 了。先通過HashCode%8 來判斷類在哪一個位置,再通過 equals 來在這個位置上尋找需要的類。對比兩個類的時候也差不多,先通過HashCode 比較,假如 HashCode 相等再判斷 equals。如果兩個類的 HashCode 都不相同,那麼這兩個類必定是不同的。

其實在 HashSet 就是採用的這種儲存和獲取方式,通過 HashCode 和 equals 組合的方式來保證集合無重複。也說明了 HashCode() 在散列表中是發揮作用的

hashCode 的正確使用

從 JDK 原始碼的註釋中可以看出,hashCode() 在散列表中才會發揮作用,當物件無需建立像 HashMap、HashSet 等集合時,可以不用重寫 hashCode() 方法,但是如果有使用到物件的雜湊集合等操作時,必須重寫 hashCode() 和 equals()。

作者:Binge 出處:http://www.cnblogs.com/binbingg/ 本文版權歸作者和部落格園共有,轉載必須給出原文連結,並保留此段宣告,否則保留追究法律責任的權利。