Java 中正確使用 hashCode 和 equals 方法

jopen 13年前發布 | 19K 次閱讀 Java Java開發

在這篇文章中,我將告訴大家我對hashCode和equals方法的理解。我將討論他們的默認實現,以及如何正確的重寫他們。我也將使用Apache Commons提供的工具包做一個實現。

目錄:

  1. hashCode()和equals()的用法
  2. 重寫默認實現
  3. 使用Apache Commons Lang包重寫hashCode()和equals()
  4. 需要注意記住的事情
  5. 當使用ORM的時候特別要注意的
  6. </ol> hashCode()和equals()定義在Object類中,這個類是所有java類的基類,所以所有的java類都繼承這兩個方法。


    使用hashCode()和equals()

    hashCode()方法被用來獲取給定對象的唯一整數。這個整數被用來確定對象被存儲在HashTable類似的結構中的位置。默認的,Object類的hashCode()方法返回這個對象存儲的內存地址的編號。

    重寫默認的實現

    如果你不重寫這兩個方法,將幾乎不遇到任何問題,但是有的時候程序要求我們必須改變一些對象的默認實現。

    來看看這個例子,讓我們創建一個簡單的類Employee

    public class Employee
    {
        private Integer id;
        private String firstname;
        private String lastName;
        private String department;

    public Integer getId() {
        return id;
    }
    public void setId(Integer 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;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String department) {
        this.department = department;
    }
    

    }</pre>上面的Employee類只是有一些非常基礎的屬性和getter、setter.現在來考慮一個你需要比較兩個employee的情形。


    public class EqualsTest {
        public static void main(String[] args) {
            Employee e1 = new Employee();
            Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);
        //Prints false in console
        System.out.println(e1.equals(e2));
    }
    

    }</pre>毫無疑問,上面的程序將輸出false,但是,事實上上面兩個對象代表的是通過一個employee。真正的商業邏輯希望我們返回true。
    為了達到這個目的,我們需要重寫equals方法。

    public boolean equals(Object o) {
            if(o == null)
            {
                return false;
            }
            if (o == this)
            {
               return true;
            }
            if (getClass() != o.getClass())
            {
                return false;
            }
            Employee e = (Employee) o;
            return (this.getId() == e.getId());
    }
    在上面的類中添加這個方法,EauqlsTest將會輸出true。
    So are we done?沒有,讓我們換一種測試方法來看看。

    import java.util.HashSet;
    import java.util.Set;

    public class EqualsTest { public static void main(String[] args) { Employee e1 = new Employee(); Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);
    
        //Prints 'true'
        System.out.println(e1.equals(e2));
    
        Set<employee> employees = new HashSet<employee>();
        employees.add(e1);
        employees.add(e2);
        //Prints two objects
        System.out.println(employees);
    }</employee></employee></pre>上面的程序輸出的結果是兩個。如果兩個employee對象equals返回true,Set中應該只存儲一個對象才對,問題在哪里呢? <br />
    

    我們忘掉了第二個重要的方法hashCode()。就像JDK的Javadoc中所說的一樣,如果重寫equals()方法必須要重寫hashCode()方法。我們加上下面這個方法,程序將執行正確。

    @Override
     public int hashCode()
     {
        final int PRIME = 31;
        int result = 1;
        result = PRIME * result + getId();
        return result;
     }
    使用Apache Commons Lang包重寫hashCode() 和equals()方法
    Apache Commons 包提供了兩個非常優秀的類來生成hashCode()和equals()方法。看下面的程序。


    import org.apache.commons.lang3.builder.EqualsBuilder;
    import org.apache.commons.lang3.builder.HashCodeBuilder;
    public class Employee
    {
     private Integer id;
     private String firstname;
     private String lastName;
     private String department;
    public Integer getId() {
        return id;
     }
     public void setId(Integer 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;
     }
     public String getDepartment() {
        return department;
     }
     public void setDepartment(String department) {
        this.department = department;
     }
    @Override
     public int hashCode()
     {
        final int PRIME = 31;
        return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
               toHashCode();
     }
    @Override
     public boolean equals(Object o) {
        if (o == null)
           return false;
        if (o == this)
           return true;
        if (o.getClass() != getClass())
           return false;
        Employee e = (Employee) o;
           return new EqualsBuilder().
                  append(getId(), e.getId()).
                  isEquals();
        }
     }
    如果你使用Eclipse或者其他的IDE,IDE也可能會提供生成良好的hashCode()方法和equals()方法。

    Java 中正確使用 hashCode 和 equals 方法

    需要注意記住的事情

    • 盡量保證使用對象的同一個屬性來生成hashCode()和equals()兩個方法。在我們的案例中,我們使用員工id。
    • eqauls方法必須保證一致(如果對象沒有被修改,equals應該返回相同的值)
    • 任何時候只要a.equals(b),那么a.hashCode()必須和b.hashCode()相等。
    • 兩者必須同時重寫。
    • </ul> 當使用ORM的時候特別要注意的

      • 如果你使用ORM處理一些對象的話,你要確保在hashCode()和equals()對象中使用getter和setter而不是直接引用成員變量。因為在ORM中有的時候成員變量會被延時加載,這些變量只有當getter方法被調用的時候才真正可用。
      • 例如在我們的例子中,如果我們使用e1.id == e2.id則可能會出現這個問題,但是我們使用e1.getId() == e2.getId()就不會出現這個問題。
      • </ul> 希望這篇文章能夠幫助你。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!