最近在讀《Effective Java》里面有很著名的一個(gè)重寫(xiě)equals和hashcode的論斷。并介紹了重寫(xiě)的原則冲甘。今天我來(lái)說(shuō)一下面試題中經(jīng)常出現(xiàn)但是我們一般理解不夠深刻的”=“绩卤、”equals“、”hsahcode“區(qū)別和聯(lián)系江醇。
基本概念
1 “==”
在java中“==”是用來(lái)比較變量值是否相等濒憋。如果是基本類(lèi)型,直接比較值陶夜。如果是對(duì)象類(lèi)型凛驮,比較的是兩個(gè)對(duì)象的引用,也就是地址条辟。對(duì)象是放在堆中的黔夭,棧中存放的是對(duì)象的引用∮鸬眨“==”是對(duì)棧中的值進(jìn)行比較的本姥。
2 “equals”
我們都知道java所有的類(lèi)都是集成自O(shè)bject類(lèi),Object里有一個(gè)方法“equals”杭棵,這個(gè)方法是用來(lái)比較兩個(gè)對(duì)象是否相等的婚惫。在Object類(lèi)中有這樣的代碼:
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è)人名懒构,由于他提出一哈希算法的概念餐济,所以就以他的名字命名了。哈希算法也稱(chēng)為散列算法胆剧,是將數(shù)據(jù)依特定算法直接指定到一個(gè)地址上絮姆。可以說(shuō)hashCode方法實(shí)際上返回的就是對(duì)象存儲(chǔ)的物理地址(實(shí)際可能并不是)秩霍。這樣一來(lái)篙悯,當(dāng)集合要添加新的元素時(shí),先調(diào)用這個(gè)元素的hashCode方法铃绒,就一下子能定位到它應(yīng)該放置的物理位置上鸽照。如果這個(gè)位置上沒(méi)有元素,它就可以直接存儲(chǔ)在這個(gè)位置上颠悬,不用再進(jìn)行任何比較了矮燎;如果這個(gè)位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進(jìn)行比較赔癌,相同的話就不存了诞外,不相同就散列其它的地址。
關(guān)系
首先說(shuō)“==”和“equals”灾票。
先明白一個(gè)事情:如果類(lèi)沒(méi)有重寫(xiě)equals浅乔,那么對(duì)于該類(lèi)的對(duì)象來(lái)說(shuō)“==”和“equals”沒(méi)有區(qū)別。都是比較對(duì)象的內(nèi)存地址铝条。 但在一些類(lèi)庫(kù)當(dāng)中這個(gè)方法被覆蓋掉了靖苇,如String,Integer,Date在這些類(lèi)當(dāng)中equals有其自身的實(shí)現(xiàn),而不再是比較類(lèi)在堆內(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ì)象值埠忘,'=='比較地址“脾拆。要看是否重寫(xiě)了equals方法馒索。再說(shuō)equals和hashCode之間的關(guān)系。首先hashCode存在就是為了提高效率并且輔助equals的名船,一般重寫(xiě)equals要同時(shí)重寫(xiě)hashCode绰上。java中這樣規(guī)定他們的關(guān)系:1、如果兩個(gè)對(duì)象相同渠驼,那么它們的hashCode值一定要相同蜈块;2、如果兩個(gè)對(duì)象的hashCode相同迷扇,它們并不一定相同 百揭,上面說(shuō)的對(duì)象相同指的是用eqauls方法比較。反過(guò)來(lái):hashcode()不等蜓席,一定能推出equals()也不等器一;hashcode()相等,equals()可能相等厨内,也可能不等祈秕。 如果重寫(xiě)這兩個(gè)方法最好遵循以上原則。所以比較兩者還要看具體是如何重寫(xiě)的雏胃。
特殊情況
說(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)存中。他們的值相同匣掸,但是位置不同趟紊。所以可見(jiàn)java定義這個(gè)緩沖池就是為了節(jié)約資源。我們?cè)谟米址臅r(shí)候 盡量采用 :String a =“”碰酝;這種形式霎匈。以上就是他們的實(shí)現(xiàn)和原理,相信現(xiàn)在應(yīng)該很清楚了送爸。