hashCode() 和equals() 区别和作用
HashSet和HashMap一直都是JDK中最常用的兩個(gè)類,HashSet要求不能存儲(chǔ)相同的對(duì)象,HashMap要求不能存儲(chǔ)相同的鍵。
那么Java運(yùn)行時(shí)環(huán)境是如何判斷HashSet中相同對(duì)象、HashMap中相同鍵的呢?當(dāng)存儲(chǔ)了“相同的東西”之后Java運(yùn)行時(shí)環(huán)境又將如何來維護(hù)呢?
在研究這個(gè)問題之前,首先說明一下JDK對(duì)equals(Object obj)和hashcode()這兩個(gè)方法的定義和規(guī)范:
在Java中任何一個(gè)對(duì)象都具備equals(Object obj)和hashcode()這兩個(gè)方法,因?yàn)樗麄兪窃贠bject類中定義的。
equals(Object obj)方法用來判斷兩個(gè)對(duì)象是否“相同”,如果“相同”則返回true,否則返回false。
hashcode()方法返回一個(gè)int數(shù),在Object類中的默認(rèn)實(shí)現(xiàn)是“將該對(duì)象的內(nèi)部地址轉(zhuǎn)換成一個(gè)整數(shù)返回”。
接下來有兩個(gè)個(gè)關(guān)于這兩個(gè)方法的重要規(guī)范(我只是抽取了最重要的兩個(gè),其實(shí)不止兩個(gè)):
規(guī)范1:若重寫equals(Object obj)方法,有必要重寫hashcode()方法,確保通過equals(Object obj)方法判斷結(jié)果為true的兩個(gè)對(duì)象具備相等的hashcode()返回值。說得簡單點(diǎn)就是:“如果兩個(gè)對(duì)象相同,那么他們的hashcode應(yīng)該 相等”。不過請(qǐng)注意:這個(gè)只是規(guī)范,如果你非要寫一個(gè)類讓equals(Object obj)返回true而hashcode()返回兩個(gè)不相等的值,編譯和運(yùn)行都是不會(huì)報(bào)錯(cuò)的。不過這樣違反了Java規(guī)范,程序也就埋下了BUG。
規(guī)范2:如果equals(Object obj)返回false,即兩個(gè)對(duì)象“不相同”,并不要求對(duì)這兩個(gè)對(duì)象調(diào)用hashcode()方法得到兩個(gè)不相同的數(shù)。說的簡單點(diǎn)就是:“如果兩個(gè)對(duì)象不相同,他們的hashcode可能相同”。
根據(jù)這兩個(gè)規(guī)范,可以得到如下推論:
1、如果兩個(gè)對(duì)象equals,Java運(yùn)行時(shí)環(huán)境會(huì)認(rèn)為他們的hashcode一定相等。
2、如果兩個(gè)對(duì)象不equals,他們的hashcode有可能相等。
3、如果兩個(gè)對(duì)象hashcode相等,他們不一定equals。
4、如果兩個(gè)對(duì)象hashcode不相等,他們一定不equals。
這樣我們就可以推斷Java運(yùn)行時(shí)環(huán)境是怎樣判斷HashSet和HastMap中的兩個(gè)對(duì)象相同或不同了。我的推斷是:先判斷hashcode是否相等,再判斷是否equals。
測試程序如下:首先我們定義一個(gè)類,重寫hashCode()和equals(Object obj)方法
Java代碼
class A { @Override public boolean equals(Object obj) { System.out.println("判斷equals"); return false; } @Override public int hashCode() { System.out.println("判斷hashcode"); return 1; } }然后寫一個(gè)測試類,代碼如下:
Java代碼
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()); } }運(yùn)行之后打印結(jié)果是:
判斷hashcode
判斷hashcode
判斷equals
2
可以看出,Java運(yùn)行時(shí)環(huán)境會(huì)調(diào)用new A()這個(gè)對(duì)象的hashcode()方法。其中:
打印出的第一行“判斷hashcode”是第一次map.put(new A(), new Object())所打印出的。
接下來的“判斷hashcode”和“判斷equals”是第二次map.put(new A(), new Object())所打印出來的。
那么為什么會(huì)是這樣一個(gè)打印結(jié)果呢?我是這樣分析的:
1、當(dāng)?shù)谝淮蝝ap.put(new A(), new Object())的時(shí)候,Java運(yùn)行時(shí)環(huán)境就會(huì)判斷這個(gè)map里面有沒有和現(xiàn)在添加的 new A()對(duì)象相同的鍵,判斷方法:調(diào)用new A()對(duì)象的hashcode()方法,判斷map中當(dāng)前是不是存在和new A()對(duì)象相同的HashCode。顯然,這時(shí)候沒有相同的,因?yàn)檫@個(gè)map中都還沒有東西。所以這時(shí)候hashcode不相等,則沒有必要再調(diào)用 equals(Object obj)方法了。參見推論4(如果兩個(gè)對(duì)象hashcode不相等,他們一定不equals)
2、當(dāng)?shù)诙蝝ap.put(new A(), new Object())的時(shí)候,Java運(yùn)行時(shí)環(huán)境再次判斷,這時(shí)候發(fā)現(xiàn)了map中有兩個(gè)相同的hashcode(因?yàn)槲抑貙懥薃類的hashcode()方 法永遠(yuǎn)都返回1),所以有必要調(diào)用equals(Object obj)方法進(jìn)行判斷了。參見推論3(如果兩個(gè)對(duì)象hashcode相等,他們不一定equals),然后發(fā)現(xiàn)兩個(gè)對(duì)象不equals(因?yàn)槲抑貙懥薳quals(Object obj)方法,永遠(yuǎn)都返回false)。
3、這時(shí)候判斷結(jié)束,判斷結(jié)果:兩次存入的對(duì)象不是相同的對(duì)象。所以最后打印map的長度的時(shí)候顯示結(jié)果是:2。
改寫程序如下:
Java代碼
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()); } }運(yùn)行之后打印結(jié)果是:
判斷hashcode
判斷hashcode
判斷equals
1
顯然這時(shí)候map的長度已經(jīng)變成1了,因?yàn)镴ava運(yùn)行時(shí)環(huán)境認(rèn)為存入了兩個(gè)相同的對(duì)象。原因可根據(jù)上述分析方式進(jìn)行分析。
以上分析的是HashMap,其實(shí)HashSet的底層本身就是通過HashMap來實(shí)現(xiàn)的,所以他的判斷原理和HashMap是一樣的,也是先判斷hashcode再判斷equals。
所以:寫程序的時(shí)候應(yīng)盡可能的按規(guī)范來,不然在不知不覺中就埋下了bug!
總結(jié)
以上是生活随笔為你收集整理的hashCode() 和equals() 区别和作用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Hibernate环境搭建以及Hello
- 下一篇: 音视频通话质量那个品牌做的更好?