1. 程式人生 > >深入淺出Java中的==,equals(), hashCode()

深入淺出Java中的==,equals(), hashCode()

前言-----

==,equals(), hashCode()是不是傻傻分不清, 相信我,  看完這篇部落格就不會了

目錄

測試類

一. 快速區分 ==和equals()的區別

二.如何把ID和Brand相同的car判定為同一物件呢?

三.什麼是hashCode值

四.hashCode值的比較結果 和 equals()的比較結果在邏輯上應該一致

五.equals()和hashCode()重寫時的注意事項


 

測試類

class Car{
	
	
	String ID;//車牌號
	String Brand;//品牌
        String mileage //行駛的公里數
	
	public Car(String ID,String Brand) {
		
		this.ID = ID;
		this.Brand = Brand;
	}
}

 

一. 快速區分 ==和equals()的區別

  • ==是判斷兩個物件的地址是否相同
  • equals()方法如果沒有重寫的話,預設也是呼叫==方法進行比較

   

package Test;

import java.util.HashMap;

public class HashMapTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Car car1 = new Car("1222","AA");
		Car car2 = new Car("1222","AA");
		
		
		System.out.println(car1 == car2);
		
		System.out.println(car1.equals(car2));
    }
}

測試結果:

由於car1和car2在在記憶體中的地址不相同,所以測試結果都是false

 

二.如何把ID和Brand相同的car判定為同一物件呢?

有時,我們需要根據物件的屬性值來判斷物件是否相同,而不是僅僅從物件的地址來判斷, 那麼如何做到呢?重寫equals()函式,

String 中用equals()比較字串內容否相同,本質上也是因為重寫了Object類中的equals()方法:

現在我們重寫Car類的equals()方法, 只要ID和Brand相同,就視為同一個物件:

package Test;

import java.util.HashMap;

public class HashMapTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Car car1 = new Car("1222","AA");
		Car car2 = new Car("1222","AA");
		
		
		System.out.println(car1 == car2);
		
		System.out.println(car1.equals(car2));
		
	}
}

class Car{
	
	
	String ID;//車牌號
	String Brand;//品牌
        String mileage //行駛的公里數
	
	public Car(String ID,String Brand) {
		
		this.ID = ID;
		this.Brand = Brand;
	}


	public boolean equals(Object obj) { //定義ID和Brand都相同的物件就是相同的
		
		return   (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
	}
}

此時測試結果是false 和true, 說明equals()已經改寫咯

 

三.什麼是hashCode值

如果把物件類比為一個學生,那麼物件的地址相當於學生的身份證,是獨一無二的;  而hashCode值相當於一個學生的姓名,可能會有多個物件擁有相同的hashCode值, 如同姓名相同.

package Test;

import java.util.HashMap;

public class HashMapTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Car car1 = new Car("1222","AA");
		Car car2 = new Car("1222","AA");
		
		
		System.out.println(car1.hashCode());
		System.out.println(car2.hashCode());
   }
}

class Car{
	
	
	String ID;//車牌號
	String Brand;//品牌
        String mileage //行駛的公里數
	
	public Car(String ID,String Brand) {
		
		this.ID = ID;
		this.Brand = Brand;
	}


	public boolean equals(Object obj) { //定義ID和Brand都相同的物件就是相同的
		
		return   (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
	}
}

測試結果:

car1的hashCode值為:2018699554
car2的hashCode值為: 1311053135

 

四.hashCode值的比較結果 和 equals()的比較結果在邏輯上應該一致

  • 為什麼要一致?

還是以上面的car1和car2為例, 我們重寫了equals()的方法,car1.equals(car2)的結果true, 但是car1和car2的hashCode值卻不同,用hashCode值判是否相同的話,結果會是false, 二者的結果是不相同的.  這在邏輯上是說不過去的, 所以我們應該另hashCode值的比較結果和equals()的比較結果在邏輯上一致 ,這也是一個良好的習慣.

 

  • 那麼如何另二者的比較結果一致呢?

重寫hashCode方法,且必須設計一種生成機制, 在這種機制下,ID 和Brand的Car物件可以擁有相同的hashCod值, 不妨設計如下:

	public int hashCode() {
		
		return ID.hashCode() + Brand.hashCode();
		
	}

測試demo:

import java.util.HashMap;

public class HashMapTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Car car1 = new Car("1222","AA");
		Car car2 = new Car("1222","AA");
		
		
		System.out.println("car1的hashCode值為:" +car1.hashCode());
		System.out.println("car2的hashCode值為:" +car2.hashCode());
		
		System.out.print("用equals()的比較結果是:");
		System.out.println( car1.equals(car2) );
		
		 System.out.print("用hashCode值的比較結果是:" );
		 System.out.println( car1.hashCode()==car2.hashCode() );
    }
}

class Car{
	
	
	String ID;//車牌號
	String Brand;//品牌
        String mileage //行駛的公里數
	
	public Car(String ID,String Brand) {
		
		this.ID = ID;
		this.Brand = Brand;
	}


	public boolean equals(Object obj) { //定義ID和Brand都相同的物件就是相同的
		
		return   (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
	}
	

	public int hashCode() {
		
		return ID.hashCode() + Brand.hashCode();
		
	}
	
	
	
}

測試結果:

car1的hashCode值為:1511489
car2的hashCode值為:1511489
用equals()的比較結果是:true
用hashCode值的比較結果是:true

 

五.equals()和hashCode()重寫時的注意事項

注:此段內容參考了該部落格,感謝

 

  • 在程式執行期間,只要equals方法的比較操作用到的資訊沒有被修改,那麼對這同一個物件呼叫多次,hashCode方法必須始終如一地返回同一個整數。
  • 如果兩個物件根據equals方法比較是相等的,那麼呼叫兩個物件的hashCode方法必須返回相同的整數結果。
  • 如果兩個物件根據equals方法比較是不等的,則hashCode方法不一定得返回不同的整數。

第二條的內容就是上面我所解釋的.

第三條的內容也好理解, 如果equals()結果是不等, 但是hashCode值有小概率是相等的.

第一條的內容: 就是說hashCode() 裡面用於生成hashCode的欄位最好是不容易改變的 ,以上面重寫的hashCode()方法為例.

	public int hashCode() {
		
		return ID.hashCode() + Brand.hashCode();
		
	}

這邊hashCode值依靠於Car的ID和Brand這兩個屬性, 如果某個時候, ID或者Brand改變了,那麼對於的hashCode值也會改變了,

這又有什麼影響呢,不妨看段程式碼:

package Test;

import java.util.HashMap;

public class HashMapTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub


		HashMap <Car, String> myMap = new HashMap <Car,String>();
		
		Car car1 = new Car("1222","AA");
		
		System.out.print("此時的hashCode值為:");
		System.out.println(car1.hashCode());
		
		myMap.put(car1, "Jane");
		
		
		car1.ID = "1111";
		System.out.print("修改ID後,hashCode值為:");
		System.out.println(car1.hashCode());

		
		//判斷是否存在key為car1的Entry
		System.out.println(myMap.get(car1)); 

		

	}
	

}
class Car{
	
	
	String ID;//車牌號
	String Brand;//品牌
        String mileage //行駛的公里數
	
	public Car(String ID,String Brand) {
		
		this.ID = ID;
		this.Brand = Brand;
	}


	public boolean equals(Object obj) { //定義ID和Brand都相同的物件就是相同的
		
		return   (ID ==((Car)obj).ID ) && (Brand ==((Car)obj).Brand);
	}
	

	public int hashCode() {
		
		return ID.hashCode() + Brand.hashCode();
		
	}
	
	
	
}

測試結果:

此時的hashCode值為:1511489
修改ID後,hashCode值為:1510496
null
 

熟悉HashMap的朋友應該很清楚,  HashMap中的get()方法是通過比較hash值來查詢的,而hash值又依賴於hashCode值,因此就不難理解為什麼結果是null了. 因為HashMap中car1的hashCode值和 修改ID後的car1的hashCode值不同了

為了儘量避免這種情況, 用於生成hashCode()的欄位在使用過程中應該是比較不會頻繁更改的欄位, 所以這也是為什麼不用 mileage (行駛的公里數)來生成hashCode值的原因了, 它更容易被更改.