2020/07/09

[筆記] Effective Java #11 覆寫 equals 時總要覆寫 hashCode

Effective Java 3rd 簡體中文版筆記 #11 覆寫 equals 時總要覆寫 hashCode
在覆寫 equals 時要一併覆寫 hashCode,如果沒有覆寫 hashCode,會造成利用 hashCode 作判斷的方法失常。
在覆寫 hashCode 時要注意幾點規定:
  1. 在不改變 equals 方法及 field 內容的前提下,每次調用同一實體的 hashCode,結果須一致。
  2. 若兩物件用 equals 比對為 true,則它們的 hashCode 必相同。a.equals(b) 為 true,那麼 a.hashCode = b.hashCode。
  3. 若兩物件用 equals 比對為 false,則它們的 hashCode 不一定要不同,就是說 hashCode 仍有機會是一樣的。a.equals(b) 為 false,a.hashCode = b.hashCode 或 a.hashCode != b.hashCode 都可以。
如果覆寫 equals 而沒有覆寫 hashCode,那就是違反第 2 項規定。Seat 表示一座位,由 roomtable 組成。
public class Seat {
    private final String room;
    private final String table;

    public Seat(String room, String table) {
        this.room = room;
        this.table = table;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Seat seat = (Seat) o;
        return room.equals(seat.room) && table.equals(seat.table);
    }
}
覆寫 equals 後,如果兩個 Seat 邏輯一樣,但沒有覆寫 hashCode,無法辨識出兩個 Seat 是一樣的,因為在 HashMap 會先用 hashCode 來判斷,當 hashCode 一樣後,才會調用 equals 來比對。
Map<Seat, String> map = new HashMap<>();
map.put(new Seat("301", "A01"), "Dana");
map.put(new Seat("301", "A01"), "Dana");
System.out.println(map.size());  // 2
如果覆寫 hashCode 後,所有 Seat 都回傳一樣的 hash code,那麼會發生什麼事? 這樣的寫法是符合上面 3 項規定的,那會有問題嗎? 有,這樣就失去 hash 的意義,hash 是希望分配,減少碰撞。如果 hashCode 全部一樣,就像是丟到同一 bucket,基本上跟沒有 hash 一樣!
Java基礎:講講 HashCode 的作用
https://www.gushiciku.cn/pl/gkK1/zh-tw
覆寫 hashCode 時要注意以下幾點:
  1. 給一個常數 c,多為質數。
  2. equals 方法中使用的每一 field,計算 hashCode。基本型態,用該型態自有的 hashCode 方法,像是 Short.hashCode(s),若為物件,則需要遞迴地找出該物件的 hashCode
  3. 將每一個 hashCode 作乘法運算並與 c 相加,返回結果。
如果要簡單點,而且速度慢一點不是問題,可以使用 Objects.hash
@Override
public int hashCode() {
    return Objects.hash(room, desk);
}
當一個 class 為不可變時,表示它的 hashCode 調用較頻繁,重複計算是耗時的,所以可以考慮將 hashCode 計算後儲存起來。另外,不要對 hashCode 的實作作保證,如果開發者或客戶依賴這樣的假設,未來要更改實作就會比較難。
// java.lang.String source code
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    ...
}
轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2020/07/effective-java-11-override-equals-and-hashcode.html

沒有留言:

張貼留言