在這篇文章中,我將告訴大家我對hashCode和equals方法的理解。我將討論他們的默認實現,以及如何正確的重寫他們。我也將使用Apache Commons提供的工具包做一個實現。
目錄:
- hashCode()和equals()的用法
- 重寫默認實現
- 使用Apache Commons Lang包重寫hashCode()和equals()
- 需要注意記住的事情
- 當使用ORM的時候特別要注意的
hashCode()和equals()定義在Object類中,這個類是所有java類的基類,所以所有的java類都繼承這兩個方法。
使用hashCode()和equals()
hashCode()方法被用來獲取給定對象的唯一整數。這個整數被用來確定對象被存儲在HashTable類似的結構中的位置。默認的,Object類的hashCode()方法返回這個對象存儲的內存地址的編號。
重寫默認的實現
如果你不重寫這兩個方法,將幾乎不遇到任何問題,但是有的時候程序要求我們必須改變一些對象的默認實現。
來看看這個例子,讓我們創建一個簡單的類Employee
04 |
private String
firstname; |
05 |
private String
lastName; |
06 |
private String
department; |
08 |
public Integer
getId() { |
11 |
public void setId(Integer
id) { |
14 |
public String
getFirstname() { |
17 |
public void setFirstname(String
firstname) { |
18 |
this .firstname
= firstname; |
20 |
public String
getLastName() { |
23 |
public void setLastName(String
lastName) { |
24 |
this .lastName
= lastName; |
26 |
public String
getDepartment() { |
29 |
public void setDepartment(String
department) { |
30 |
this .department
= department; |
上面的Employee類只是有一些非常基礎的屬性和getter、setter.現在來考慮一個你需要比較兩個employee的情形。
01 |
public class EqualsTest
{ |
02 |
public static void main(String[]
args) { |
03 |
Employee
e1 = new Employee(); |
04 |
Employee
e2 = new Employee(); |
09 |
System.out.println(e1.equals(e2)); |
毫無疑問,上面的程序將輸出false,但是,事實上上面兩個對象代表的是通過一個employee。真正的商業邏輯希望我們返回true。
爲了達到這個目的,我們需要重寫equals方法。
01 |
public boolean equals(Object
o) { |
10 |
if (getClass()
!= o.getClass()) |
14 |
Employee
e = (Employee) o; |
15 |
return ( this .getId()
== e.getId()); |
在上面的類中添加這個方法,EauqlsTest將會輸出true。
So are we done?沒有,讓我們換一種測試方法來看看。
01 |
import java.util.HashSet; |
04 |
public class EqualsTest |
06 |
public static void main(String[]
args) |
08 |
Employee
e1 = new Employee(); |
09 |
Employee
e2 = new Employee(); |
15 |
System.out.println(e1.equals(e2)); |
17 |
Set<Employee>
employees = new HashSet<Employee>(); |
21 |
System.out.println(employees); |
上面的程序輸出的結果是兩個。如果兩個employee對象equals返回true,Set中應該只存儲一個對象纔對,問題在哪裏呢?
我們忘掉了第二個重要的方法hashCode()。就像JDK的Javadoc中所說的一樣,如果重寫equals()方法必須要重寫hashCode()方法。我們加上下面這個方法,程序將執行正確。
6 |
result
= PRIME * result + getId(); |
使用Apache Commons Lang包重寫hashCode() 和equals()方法
Apache Commons 包提供了兩個非常優秀的類來生成hashCode()和equals()方法。看下面的程序。
01 |
import org.apache.commons.lang3.builder.EqualsBuilder; |
02 |
import org.apache.commons.lang3.builder.HashCodeBuilder; |
06 |
private String
firstname; |
07 |
private String
lastName; |
08 |
private String
department; |
09 |
public Integer
getId() { |
12 |
public void setId(Integer
id) { |
15 |
public String
getFirstname() { |
18 |
public void setFirstname(String
firstname) { |
19 |
this .firstname
= firstname; |
21 |
public String
getLastName() { |
24 |
public void setLastName(String
lastName) { |
25 |
this .lastName
= lastName; |
27 |
public String
getDepartment() { |
30 |
public void setDepartment(String
department) { |
31 |
this .department
= department; |
37 |
return new HashCodeBuilder(getId()% 2 == 0 ?getId()+ 1 :getId(),
PRIME). |
41 |
public boolean equals(Object
o) { |
46 |
if (o.getClass()
!= getClass()) |
48 |
Employee
e = (Employee) o; |
49 |
return new EqualsBuilder(). |
50 |
append(getId(),
e.getId()). |
如果你使用Eclipse或者其他的IDE,IDE也可能會提供生成良好的hashCode()方法和equals()方法。
需要注意記住的事情
- 儘量保證使用對象的同一個屬性來生成hashCode()和equals()兩個方法。在我們的案例中,我們使用員工id。
- eqauls方法必須保證一致(如果對象沒有被修改,equals應該返回相同的值)
- 任何時候只要a.equals(b),那麼a.hashCode()必須和b.hashCode()相等。
- 兩者必須同時重寫。
當使用ORM的時候特別要注意的
- 如果你使用ORM處理一些對象的話,你要確保在hashCode()和equals()對象中使用getter和setter而不是直接引用成員變量。因爲在ORM中有的時候成員變量會被延時加載,這些變量只有當getter方法被調用的時候才真正可用。
- 例如在我們的例子中,如果我們使用e1.id == e2.id則可能會出現這個問題,但是我們使用e1.getId() == e2.getId()就不會出現這個問題。
希望這篇文章能夠幫助你。