最近在讀《Effective Java》里面有很著名的一個(gè)重寫equals和hashcode的論斷。并介紹了重寫的原則。今天我來(lái)說(shuō)一下面試題中經(jīng)常出現(xiàn)但是我們一般理解不夠深刻的”=“、”equals“、”hsahcode“區(qū)別和聯(lián)系。
基本概念
1 “==”
在java中“==”是用來(lái)比較變量值是否相等。如果是基本類型,直接比較值。如果是對(duì)象類型,比較的是兩個(gè)對(duì)象的引用,也就是地址。對(duì)象是放在堆中的,棧中存放的是對(duì)象的引用?!?=”是對(duì)棧中的值進(jìn)行比較的。
2 “equals”
我們都知道java所有的類都是集成自O(shè)bject類,Object里有一個(gè)方法“equals”,這個(gè)方法是用來(lái)比較兩個(gè)對(duì)象是否相等的。在Object類中有這樣的代碼:
public boolean equals(Object o) {
return this == o;
}
說(shuō)明在Object里"equals"和"=="是一回事。
3 “hashcode”
在Object里提供了hashcode這個(gè)方法。要說(shuō)hashcode就得說(shuō)java集合。java有的集合是不能重復(fù)的,所以需要用equeals判斷集合中元素是否是同一個(gè)。但是如果集合中現(xiàn)在已經(jīng)有1000個(gè)元素,那么第1001個(gè)元素加入集合時(shí),它就要調(diào)用1000次equals方法。這顯然會(huì)大大降低效率。于是,Java采用了哈希表的原理。哈希(Hash)實(shí)際上是個(gè)人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。哈希算法也稱為散列算法,是將數(shù)據(jù)依特定算法直接指定到一個(gè)地址上??梢哉f(shuō)hashCode方法實(shí)際上返回的就是對(duì)象存儲(chǔ)的物理地址(實(shí)際可能并不是)。這樣一來(lái),當(dāng)集合要添加新的元素時(shí),先調(diào)用這個(gè)元素的hashCode方法,就一下子能定位到它應(yīng)該放置的物理位置上。如果這個(gè)位置上沒有元素,它就可以直接存儲(chǔ)在這個(gè)位置上,不用再進(jìn)行任何比較了;如果這個(gè)位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進(jìn)行比較,相同的話就不存了,不相同就散列其它的地址。
關(guān)系
首先說(shuō)“==”和“equals”。
先明白一個(gè)事情:如果類沒有重寫equals,那么對(duì)于該類的對(duì)象來(lái)說(shuō)“==”和“equals”沒有區(qū)別。都是比較對(duì)象的內(nèi)存地址。 但在一些類庫(kù)當(dāng)中這個(gè)方法被覆蓋掉了,如String,Integer,Date在這些類當(dāng)中equals有其自身的實(shí)現(xiàn),而不再是比較類在堆內(nèi)存中的存放地址了。所以會(huì)有很多經(jīng)典的面試題:
String a = new String ("abc");
String b = new String ("abc");
System.out.println(a.equals(b));
System.out.println(a==b);
結(jié)果大家都知道 是true和false,其實(shí)這才是特殊情況。并不能得出一般的結(jié)論而籠統(tǒng)的說(shuō):”equals比較對(duì)象值,'=='比較地址“。要看是否重寫了equals方法。再說(shuō)equals和hashCode之間的關(guān)系。首先hashCode存在就是為了提高效率并且輔助equals的,一般重寫equals要同時(shí)重寫hashCode。java中這樣規(guī)定他們的關(guān)系:1、如果兩個(gè)對(duì)象相同,那么它們的hashCode值一定要相同;2、如果兩個(gè)對(duì)象的hashCode相同,它們并不一定相同 ,上面說(shuō)的對(duì)象相同指的是用eqauls方法比較。反過(guò)來(lái):hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。 如果重寫這兩個(gè)方法最好遵循以上原則。所以比較兩者還要看具體是如何重寫的。
特殊情況
說(shuō)一種特殊情況:
String s1 = "Lpnpcs";
String s2 = "Lpnpcs";
if (s1 == s2) {
System.out.println("s1 == s2");
} else{
System.out.println("s1 != s2");
}輸出s1==s2;
String s1 = "Lpnpcs";
String s2 = new String("Lpnpcs");
if (s1 == s2)
{System.out.println("s1 == s2");}
else
{System.out.println("s1 != s2");}
if (s1.equals(s2)) {System.out.println("s1 equals s2");}
else{
System.out.println("s1 not equals s2");
}
輸出s1 != s2 s1 equals s2說(shuō)明:s1 s2分別引用了兩個(gè)"Lpnpcs"String對(duì)象。這是什么情況? 這是由于java中對(duì)于字符串定義了一個(gè)字符串緩沖池,程序在運(yùn)行的時(shí)候會(huì)創(chuàng)建一個(gè)字符串緩沖池當(dāng)使用 s2 = "Lpnpcs" 這樣的表達(dá)是創(chuàng)建字符串的時(shí)候,程序首先會(huì)在這個(gè)String緩沖池中尋找相同值的對(duì)象,在第一個(gè)程序中,s1先被放到了池中,所以在s2被創(chuàng)建的時(shí)候,程序找到了具有相同值的 s1將s2引用s1所引用的對(duì)象"Lpnpcs"。第二段程序中,使用了 new 操作符,他明白的告訴程序:"我需要新建一個(gè)新的"于是一個(gè)新的"Lpnpcs"String對(duì)象被創(chuàng)建在內(nèi)存中。他們的值相同,但是位置不同。所以可見java定義這個(gè)緩沖池就是為了節(jié)約資源。我們?cè)谟米址臅r(shí)候 盡量采用 :String a =“”;這種形式。以上就是他們的實(shí)現(xiàn)和原理,相信現(xiàn)在應(yīng)該很清楚了。