1. 程式人生 > >hashCode 和 equals 正確用法和需要注意的地方(面試基本都會問)

hashCode 和 equals 正確用法和需要注意的地方(面試基本都會問)

文章來源:

文章目錄:

1、hashCode()和equals()的用法
2、重寫預設實現
3、使用Apache Commons Lang包重寫hashCode()和equals()
4、需要注意記住的事情
5、當使用ORM的時候特別要注意的

hashCode()和equals()定義在Object類中,這個類是所有java類的基類,所以所有的java類都繼承這兩個方法。

1、使用hashCode()和equals()方法

hashCode()方法被用來獲取給定物件的唯一整數。這個整數被用來確定物件被儲存在HashTable類似的結構中的位置。預設的,Object類的hashCode()方法返回這個物件儲存的記憶體地址的編號。

2、重寫預設的實現

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

來看看這個例子,讓我們建立一個簡單的類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; } }

上面的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));
    }
}

毫無疑問,上面的程式將輸出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物件equals返回true,Set中應該只儲存一個物件才對,問題在哪裡呢?
我們忘掉了第二個重要的方法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()方法。

這裡寫圖片描述

3、需要注意記住的事情

(1)、儘量保證使用物件的同一個屬性來生成hashCode()和equals()兩個方法。在我們的案例中,我們使用員工id。

(2)、eqauls方法必須保證一致(如果物件沒有被修改,equals應該返回相同的值)

(3)、任何時候只要a.equals(b),那麼a.hashCode()必須和b.hashCode()相等。

(4)、兩者必須同時重寫。

4、當使用ORM的時候特別要注意的

如果你使用ORM處理一些物件的話,你要確保在hashCode()和equals()物件中使用getter和setter而不是直接引用成員變數。因為在ORM中有的時候成員變數會被延時載入,這些變數只有當getter方法被呼叫的時候才真正可用。例如在我們的例子中,如果我們使用e1.id == e2.id則可能會出現這個問題,但是我們使用e1.getId() == e2.getId()就不會出現這個問題。