淺談equals() 與 hashcode()
專案閒暇,讀書所獲。
equals()是根類Object中的方法。原始碼如下:
public boolean equals(Object obj) {
return (this == obj);
}
預設的equals()方法,直接呼叫==,比較物件在JVM中的地址。
再來談談和equals()方法較為相似的hashcode()方法:
(1)hashcode如何形成?
hashcode代表物件在hash表中的位置,通過物件的實體地址(存放在記憶體中的地址)轉換成一個整數,然後該整數通過hash函式的演算法就得到了hashcode。
(2)hashcode有什麼作用?
hashcode的存在主要是為了查詢的快捷性。hashcode是用來在雜湊儲存結構中確定物件的儲存地址的(用hashcode來代表物件在hash表中的位置)。java集合中有一類是set,特點是元素無序,但元素不可重複,判斷重複的方法是Object.equals()方法。元素很多時,就需要比較很多次,效率較低。但是,當先呼叫元素的hashCode()方法時,就能定位到它對應放置的物理儲存位置,如果這個位置上沒有元素,可直接放置,如果有元素,就呼叫它的equals()方法與他們進行比較,出現相同的,就不儲存,如果不相同就雜湊其他的地址。那麼採用hashCode(),比較次數會大大減少,效率會提高。
所以,可以得出,如果兩個物件相同,那麼他們的hashcode值一定相同;如果兩個物件的hashcode值不同,那麼兩個物件肯定不相等。當需要大量並且快速的對比的話,首先用hashCode()去對比,如果hashcode不一樣,那麼兩物件肯定不一樣,也就沒必要再去用equals()進行比較了,如果兩物件的hashcode值一樣,那麼再用equals()去進行比較,如果equals()比較下來也相同,那麼兩物件就真的相同了。這樣不僅保證了對比的準確性,更重要的是效率也大大增加。
hashCode()也是根類Object中的方法,由上面所述可以看出,兩者的作用是大致一樣的,都是實現物件的對比。但是在實際情況下,由於我們在一個類中new兩個物件,其在記憶體裡的地址肯定不同,兩物件的對比就沒有什麼意義了。所以equals()和hashCode()經常要被重寫。
下面用具體的例子來重寫的equals()和hashCode()方法。
重寫equals()方法:
定義Person類,在其中重寫equals()方法。
public class Person {
private String name;
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj){
if(obj instanceof Person){
Person person = (Person) obj;
return name.equals(person.getName()) ;
}
return false;
}
}
定義EqualsTest.java,測試檢視結果。
public class EqualsTest {
public static void main(String[] args) {
Person p1 = new Person("張三");
Person p2 = new Person("張三");
System.out.println(p1.equals(p2));
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
}
}
輸出結果為
可見,兩物件相同,其對應的hashcode也不一樣。
在散列表中,如果要判斷兩個物件是否相等,除了要覆蓋equals()方法,還要覆蓋hashCode(),否則equals()方法無效。我們將兩個Person物件放入HashSet中,並觀察其輸出結果。
public class EqualsTest {
public static void main(String[] args) {
Person p1 = new Person("張三");
Person p2 = new Person("張三");
HashSet<Person> set = new HashSet<Person>();
set.add(p1);
set.add(p2);
for(Person person : set){
System.out.print(person.getName()+" ") ;
}
System.out.println();
System.out.println(p1.equals(p2));
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
}
}
輸出結果為
我們只是重寫了equals()方法,但是對於一個HashSet來講,元素是不允許重複的,出現這種現象的原因是雖然p1和p2的內容相同,但是hashcode不一樣,所以HashSet在新增元素時,認為兩者並不相等,HashSet會將它們儲存在不同的位置,依然可以新增成功。
當我們同時覆蓋equals()和hashCode()方法時,
public class Person {
private String name;
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj){
if(obj instanceof Person){
Person person = (Person) obj;
return name.equals(person.getName()) ;
}
return false;
}
@Override
public int hashCode(){
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
輸出結果為
此時HashSet沒有重複的元素,符合set的特性。HashSet集合判斷兩個元素相等的標準是兩個物件通過equals()方法比較相等,並且兩個物件的hashCode()方法返回值也相等。
日ji