java利用hashcode判斷物件
轉載:http://myhadoop.iteye.com/blog/2059833
HashSet和HashMap一直都是JDK中最常用的兩個類,HashSet要求不能儲存相同的物件,HashMap要求不能儲存相同的鍵。
那麼Java執行時環境是如何判斷HashSet中相同物件、HashMap中相同鍵的呢?當儲存了“相同的東西”之後Java執行時環境又將如何來維護呢?
在研究這個問題之前,首先說明一下JDK對equals(Object obj)和hashcode()這兩個方法的定義和規範:
在Java中任何一個物件都具備equals(Object obj)和hashcode()這兩個方法,因為他們是在Object類中定義的。
equals(Object obj)方法用來判斷兩個物件是否“相同”,如果“相同”則返回true,否則返回false。
hashcode()方法返回一個int數,在Object類中的預設實現是“將該物件的內部地址轉換成一個整數返回”。
接下來有兩個個關於這兩個方法的重要規範(我只是抽取了最重要的兩個,其實不止兩個):
規範1:若重寫equals(Object obj)方法,有必要重寫hashcode()方法,確保通過equals(Object obj)方法判斷結果為true的兩個物件具備相等的hashcode()返回值。說得簡單點就是:“如果兩個物件相同,那麼他們的hashcode應該 相等”。不過請注意:這個只是規範,如果你非要寫一個類讓equals(Object obj)返回true而hashcode()返回兩個不相等的值,編譯和執行都是不會報錯的。不過這樣違反了Java規範,程式也就埋下了BUG。
規範2:如果equals(Object obj)返回false,即兩個物件“不相同”,並不要求對這兩個物件呼叫hashcode()方法得到兩個不相同的數。說的簡單點就是:“如果兩個物件不相同,他們的hashcode可能相同”。
根據這兩個規範,可以得到如下推論:
1、如果兩個物件equals,Java執行時環境會認為他們的hashcode一定相等。
2、如果兩個物件不equals,他們的hashcode有可能相等。
3、如果兩個物件hashcode相等,他們不一定equals。
4、如果兩個物件hashcode不相等,他們一定不equals。
這樣我們就可以推斷Java執行時環境是怎樣判斷HashSet和HastMap中的兩個物件相同或不同了。我的推斷是:先判斷hashcode是否相等,再判斷是否equals。
測試程式如下:首先我們定義一個類,重寫hashCode()和equals(Object obj)方法
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判斷equals");
return false;
}
@Override
public int hashCode() {
System.out.println("判斷hashcode");
return 1;
}
}
然後寫一個測試類,程式碼如下:
Java程式碼- publicclass Test {
- publicstaticvoid main(String[] args) {
- Map<A,Object> map = new HashMap<A, Object>();
- map.put(new A(), new Object());
- map.put(new A(), new Object());
-
System.out.println(map.size());
- }
- }
執行之後列印結果是:
判斷hashcode
判斷hashcode
判斷equals
2
可以看出,Java執行時環境會呼叫new A()這個物件的hashcode()方法。其中:
打印出的第一行“判斷hashcode”是第一次map.put(new A(), new Object())所打印出的。
接下來的“判斷hashcode”和“判斷equals”是第二次map.put(new A(), new Object())所打印出來的。
那麼為什麼會是這樣一個列印結果呢?我是這樣分析的:
1、當第一次map.put(new A(), new Object())的時候,Java執行時環境就會判斷這個map裡面有沒有和現在新增的 new A()物件相同的鍵,判斷方法:呼叫new A()物件的hashcode()方法,判斷map中當前是不是存在和new A()物件相同的HashCode。顯然,這時候沒有相同的,因為這個map中都還沒有東西。所以這時候hashcode不相等,則沒有必要再呼叫 equals(Object obj)方法了。參見推論4(如果兩個物件hashcode不相等,他們一定不equals)
2、當第二次map.put(new A(), new Object())的時候,Java執行時環境再次判斷,這時候發現了map中有兩個相同的hashcode(因為我重寫了A類的hashcode()方 法永遠都返回1),所以有必要呼叫equals(Object obj)方法進行判斷了。參見推論3(如果兩個物件hashcode相等,他們不一定equals),然後發現兩個物件不equals(因為我重寫了equals(Object obj)方法,永遠都返回false)。
3、這時候判斷結束,判斷結果:兩次存入的物件不是相同的物件。所以最後列印map的長度的時候顯示結果是:2。
改寫程式如下:
import java.util.HashMap;
import java.util.Map;
class A {
@Override
public boolean equals(Object obj) {
System.out.println("判斷equals");
return true;
}
@Override
public int hashCode() {
System.out.println("判斷hashcode");
return 1;
}
}
public class Test {
public static void main(String[] args) {
Map<A,Object> map = new HashMap<A, Object>();
map.put(new A(), new Object());
map.put(new A(), new Object());
System.out.println(map.size());
}
}
執行之後列印結果是:
判斷hashcode
判斷hashcode
判斷equals
1
顯然這時候map的長度已經變成1了,因為Java執行時環境認為存入了兩個相同的物件。原因可根據上述分析方式進行分析。
以上分析的是HashMap,其實HashSet的底層本身就是通過HashMap來實現的,所以他的判斷原理和HashMap是一樣的,也是先判斷hashcode再判斷equals。