1. 程式人生 > >如何覆寫java中的equals和hashcode方法

如何覆寫java中的equals和hashcode方法

          這篇文章算是一個翻譯,原文在:點選開啟連結,但我並沒有一字不差全部翻譯,只是選出一些重點,大家有興趣可以看看原文

          Equals和 hashCode是java中一個物件的兩個基本方法和core java的重要組成部分。Equals用來比較物件的相等性,hashcode用來生成相應物件的整數形編碼。EqualshashCodecore java中有廣泛的應用,例如在hashmap中插入和取回資料。

Equals方法覆寫遵循以下規則:

(1)自反性。一個物件必須和它自身相等;

(2)  對稱性。如果a.equals(b) 為true 那麼 b.equals(a) 也必須為true;

(3)  傳遞性。如果 a.equals(b) 為true 並且 b.equals(c) 為 true 那麼 c.equals(a) 也必須 為 true;

(4)  一致性。除非欄位的值改變,否則多次呼叫equals()方法應該返回相同的結果。

(5)  Null比較。任何物件和null比較必須返回false,並且不能夠出現NullPointerException結果。

Equals hashCode的關係

Equals()hashcode()必須遵守以下規則:

(1)         如果兩個物件執行equals()方法是相等的,那麼執行hashcode()方法的結果也必須是相等的;

(2)         如果兩個物件執行equals()方法不相等,那麼執行hashcode()方法的結果可以相等頁可以不相等。

重寫equals方法的步驟

這是大多數java程式設計師重寫equals方法的標準做法:

(1)  做this檢查。如果是this則返回true;

(2)  做null檢查。如果是null則返回false;

(3) instanceof檢查。如果instanceof返回false,那麼equals方法就返回false。在做了一些研究之後,我發現可以用getClass()方法代替instanceof來比較型別的相等性,因為instanceof在檢查子類的時候也會返回

true。所以在需要商業邏輯的時候它並不是一個嚴格的相等關係。如果你的類是不變類,沒有類會繼承它,那麼使用instanceof時合適的。所以可以使用一下方式代替instanceof :

if((obj == null) || (obj.getClass() != this.getClass()))
        return false;

(4)  物件型別轉換。

(5)  以數值型屬性開始比較每個屬性值,因為數值型屬性比較最快而且在結合檢查的時候可以使用短路操作。如果第一個屬性不匹配,那麼就可以返回false而不需要匹配剩餘的屬性。在每個屬性呼叫equals方法前也要記得做null檢查,避免在遞迴equals檢查時出現NullPointerException

覆寫equlas方法的程式碼樣例

/** 
 * Person class with equals and hashcode implementation in Java
 * @author Javin Paul
 */
public class Person {
    private int id;
    private String firstName;
    private String lastName;

    public int getId() { return id; }
    public void setId(int id) { this.id = id;}

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || obj.getClass() != this.getClass()) {
            return false;
        }

        Person guest = (Person) obj;
        return id == guest.id
                && (firstName == guest.firstName 
                     || (firstName != null && firstName.equals(guest.getFirstName())))
                && (lastName == guest.lastName 
                     || (lastName != null && lastName .equals(guest.getLastName())));
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((firstName == null) ? 0 : firstName.hashCode());
        result = prime * result + id;
        result = prime * result
                + ((lastName == null) ? 0 : lastName.hashCode());
        return result;
    }
    
}
在覆寫equals時的常見錯誤

(1)  過載了equals方法而沒有重寫它

這是我見到的覆寫equals方法最常見的錯誤。equasl的語法是public boolean equals(Object obj),但許多人無意間過載了equals方法 public boolean equals(Person obj)。這個錯誤因為靜態繫結而非常難以察覺。

(2)  第二個錯誤是在覆寫equals方法時沒有為成員變數沒有做null檢查,最終在呼叫equals方法時導致 NullPointerException。正確的做法是:

firstname == guest.firstname || (firstname !null && firstname.equals(guest.firstname)));

(3)  只覆寫equals()方法而沒有覆寫hashCode方法。你必須同時覆寫equals和hashCode的方法,要不然這個物件就不能在HashMap中作為一個key,因為HashMap是依賴這兩個方法的。

(4)      最後一個錯誤是在覆寫equals方法的時候沒有保持equals方法和compareTo()的一致性,這不是正常的要求,只是為了服從Set的約定避免重複。SortedSet 的實現例如TreeSet使用compareTo方法來比較兩個物件,例如字串。如果compareTo方法和equals方法沒有保持一致,那麼TreeSet就會允許重複,這樣就損壞了Set不能重複的約定。想要學習更多可以檢視Things to remember whileoverriding compareTo in Java

在寫equals方法時的5個小提示

1)大多數的IDE,例如NetBeans, Eclipse 和 IntelliJ IDEA提供生成equals和hashCode方法的支援。在In Eclipse do the right click-> source-> generate hashCode() and equals().

2)如果你的類中有一些唯一的商業主鍵,那麼在equals方法中比較這些主鍵欄位就足夠了而不需要比較所有的欄位。例如“id”是每個Person唯一的,那麼只需要比較id就可以鑑別兩個Person是不是相同的。

3)在覆寫hashCode方法時,要確保你使用的所有欄位都是在equals方法裡是相同的。

4)String和包裝類例如Integer,Float和Double需要覆寫equals方法而StringBuffer不需要。

5)只要有可能,要努力通過final使你的欄位不可變。Equals方法在不可變欄位上要比可變欄位安全的多。