java中正確使用hashCode和equals方法
阿新 • • 發佈:2018-11-01
Java 中正確使用 hashCode 和 equals 方法
轉載自:[開源中國社](http://www.oschina.net/question/82993_75533)
在這篇文章中,我將告訴大家我對hashCode和equals方法的理解。我將討論他們的預設實現,以及如何正確的重寫他們。我也將使用Apache Commons提供的工具包做一個實現。目錄:
- hashCode()和equals()的用法
- 重寫預設實現
- 使用Apache Commons Lang包重寫hashCode()和equals()
- 需要注意記住的事情
- 當使用ORM的時候特別要注意的
使用hashCode()和equals()
hashCode()方法被用來獲取給定物件的唯一整數。這個整數被用來確定物件被儲存在HashTable類似的結構中的位置。預設的,Object類的hashCode()方法返回這個物件儲存的記憶體地址的編號。
重寫預設的實現
如果你不重寫這兩個方法,將幾乎不遇到任何問題,但是有的時候程式要求我們必須改變一些物件的預設實現。
來看看這個例子,讓我們建立一個簡單的類Employee
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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;
}
}
|
?
1 2 3 4 5 6 7 8 9 10 11 |
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));
}
}
|
為了達到這個目的,我們需要重寫equals方法。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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());
}
|
So are we done?沒有,讓我們換一種測試方法來看看。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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);
}
|
我們忘掉了第二個重要的方法hashCode()。就像JDK的Javadoc中所說的一樣,如果重寫equals()方法必須要重寫hashCode()方法。我們加上下面這個方法,程式將執行正確。 ?
1 2 3 4 5 6 7 8 |
@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()方法。看下面的程式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
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();
}
}
|
需要注意記住的事情
- 儘量保證使用物件的同一個屬性來生成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()就不會出現這個問題。