1. 程式人生 > >淺談equals() 與 hashcode()

淺談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