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()就不會出現這個問題。