equals()和hashCode()面試考點(diǎn)

1 問(wèn)題的提出
2 equals()和hashCode()區(qū)別
3 為什么選擇hashcode方法
4 為什么要重寫(xiě)equals方法
5 改寫(xiě)equals時(shí)總是要改寫(xiě)hashcode
6 hashCode方法使用介紹

1 問(wèn)題的提出

首先equals和==最大的區(qū)別是一個(gè)是方法一個(gè)是運(yùn)算符窥妇,在Java中,二者比較的都是物理地址 而不是值得比較烹骨。

舉個(gè)例子

Student student1 = new Student();
Student student2 = new Student();
System.out.println(student1.equals(student2));
System.out.println(student1 == student2);

不論是用的是哪一種方法 沮焕, 最終的結(jié)果顯示的都是false拉宗,大家不妨可以試一下。為什么呢急灭?就是因?yàn)樗麄儽容^的不是對(duì)象中字段的值或者說(shuō)本身對(duì)象的值葬馋,而比較的是物理地址点楼。

再來(lái)舉一個(gè)例子

String a = new String("a");
String b = new String("a");
System.out.println(a == b);
System.out.println(a.equals(b));

當(dāng)我們創(chuàng)建2個(gè)String對(duì)象是 我們會(huì)發(fā)現(xiàn) 執(zhí)行的結(jié)果是 false true白对。為什么這次euqals返回的值編程了true蟀瞧?因?yàn)榇藭r(shí)equals方法不單單是比較物理地址 同時(shí)也比較了值悦污。在String中 equals方法被重寫(xiě) 當(dāng)物理地址不同時(shí)钉蒲,會(huì)進(jìn)一步比較值顷啼。

那么問(wèn)題來(lái)了 當(dāng)我調(diào)用

System.out.println(student1.toString().equals(student2.toString()));

結(jié)果卻返回了false.為什么呢?這就牽扯到了hashcode的問(wèn)題。

那么為了保證兩個(gè)對(duì)象比較值相等有什么辦法么躬厌?想必大家都試過(guò)重寫(xiě)equals方法扛施,而最終的結(jié)果都不如人意疙渣。為什么昌阿?因?yàn)閱螁沃貙?xiě)equals方法并不能改變hashcode值懦冰,在java中 首先比較的就是hashcode。

2 equals()笋颤、hashCode()區(qū)別 和 equals()伴澄、==之間的區(qū)別

equals():反映的是對(duì)象或變量具體的值非凌。注意:對(duì)象里面包含的值:可能是對(duì)象的引用類型的值敞嗡,也可能是值類型的值航背。

hashCode():計(jì)算出對(duì)象實(shí)例的哈希碼玖媚,并返回哈希碼今魔,又稱為散列函數(shù)。根類Object的hashCode()方法的計(jì)算依賴于對(duì)象實(shí)例的內(nèi)存地址咏删,故每個(gè)Object對(duì)象的hashCode都是唯一的督函。

== 比較的是變量(棧)內(nèi)存中存放的對(duì)象的(堆)內(nèi)存地址辰狡,用來(lái)判斷兩個(gè)對(duì)象的地址是否相同垄分,即是否是指相同一個(gè)對(duì)象薄湿。比較的是真正意義上的指針操作吆倦。Object中的equals方法返回的是==的判斷。

String s="abce"是一種非常特殊的形式,和new 有本質(zhì)的區(qū)別晌梨。它是java中唯一不需要new 就可以產(chǎn)生對(duì)象的途徑须妻。String s="abce"JVM會(huì)在常量池中先查找有有沒(méi)有一個(gè)值為"abcd"的對(duì)象,如果有,就會(huì)把它賦給當(dāng)前引用.即原來(lái)那個(gè)引用和現(xiàn)在這個(gè)引用指點(diǎn)向了同一對(duì)象,如果沒(méi)有,則在常量池中新創(chuàng)建一個(gè)"abcd",下一次如果有String s1 = "abcd";又會(huì)將s1指向"abcd"這個(gè)對(duì)象,即以這形式聲明的字符串,只要值相等,任何多個(gè)引用都指向同一對(duì)象.

public class test1 {
    public static void main(String[] args) {
        String a = new String("ab"); // a 為一個(gè)引用
        String b = new String("ab"); // b為另一個(gè)引用,對(duì)象的內(nèi)容一樣
        String aa = "ab"; // 放在常量池中
        String bb = "ab"; // 從常量池中查找
        if (aa == bb) // true
            System.out.println("aa==bb");
        if (a == b) // false敛惊,非同一對(duì)象
            System.out.println("a==b");
        if (a.equals(b)) // true
            System.out.println("aEQb");
        if (42 == 42.0) { // true
            System.out.println("true");
        }
    }
}
(1)為什么需要hashcode方法豆混?

因?yàn)樵谂康膶?duì)象比較中皿伺,hashCode要比equals速度更快效率更高盒粮,所以很多集合都用到了hashCode妒穴,比如HashTable摊崭。

對(duì)象的比較的原則如下:
兩個(gè)obj呢簸,如果equals()相等根时,hashCode()一定相等。
兩個(gè)obj,如果hashCode()相等替裆,equals()不一定相等(Hash散列值有沖突的情況,雖然概率很低)惠赫。

所以在集合中,判斷兩個(gè)對(duì)象是否相等的規(guī)則是:

  • 如果hashCode()相等倍阐,則查看equals()是否相等峰搪,相等則為同一對(duì)象凯旭。
  • 查看equals()是否相等罐呼,如果相等,則兩obj相等。
(2)二者的聯(lián)系和區(qū)別

1 首先equals()和hashcode()這兩個(gè)方法都是從object類中繼承過(guò)來(lái)的
equals()是對(duì)兩個(gè)對(duì)象的地址值進(jìn)行的比較(即比較引用是否相同)夯尽。
hashCode()是一個(gè)本地方法匙握,它的實(shí)現(xiàn)是根據(jù)本地機(jī)器相關(guān)的陈轿。

2 Java要求equals()必須遵循:(1)對(duì)稱性麦射;(2)反射性法褥;(3)類推性;(4)一致性揍愁;

3 equals()相等的兩個(gè)對(duì)象莽囤,hashcode()一定相等朽缎;
反過(guò)來(lái):hashcode()不等,一定能推出equals()也不等;
hashcode()相等最筒,equals()可能相等床蜘,也可能不等邢锯。

3 為什么選擇hashcode方法

以java.lang.Object來(lái)理解,JVM每new一個(gè)Object,它都會(huì)將這個(gè)Object丟到一個(gè)Hash哈希表中去,這樣的話,下次做Object的比較或者取這個(gè)對(duì)象的時(shí)候,它會(huì)根據(jù)對(duì)象的hashcode再?gòu)腍ash表中取這個(gè)對(duì)象丹擎。這樣做的目的是提高取對(duì)象的效率鸥鹉。

具體過(guò)程如下:

  1. new Object(),JVM根據(jù)這個(gè)對(duì)象的Hashcode值,放入到對(duì)應(yīng)的Hash表對(duì)應(yīng)的Key上,如果不同的對(duì)象確產(chǎn)生了相同的hash值,也就是發(fā)生了Hash key相同導(dǎo)致沖突的情況府适,那么就在這個(gè)Hash key的地方產(chǎn)生一個(gè)鏈表,將所有產(chǎn)生相同hashcode的對(duì)象放到這個(gè)單鏈表上去檐春,串在一起么伯。

  2. 比較兩個(gè)對(duì)象的時(shí)候,首先根據(jù)他們的hashcode去hash表中找他的對(duì)象,當(dāng)兩個(gè)對(duì)象的hashcode相同,那么就是說(shuō)他們這兩個(gè)對(duì)象放在Hash表中的同一個(gè)key上,那么他們一定在這個(gè)key上的鏈表上。那么此時(shí)就只能根據(jù)Object的equal方法來(lái)比較這個(gè)對(duì)象是否equal骨望。當(dāng)兩個(gè)對(duì)象的hashcode不同的話擎鸠,肯定他們不能equal缘圈。

舉個(gè)例子:list是可以重復(fù)的糟把,set是不可以重復(fù)的垂寥。那么set存儲(chǔ)數(shù)據(jù)的時(shí)候是怎樣判斷存進(jìn)的數(shù)據(jù)是否已經(jīng)存在另锋。使用equals()方法呢夭坪,還是hashcode()方法室梅。

假如用equals()疚宇,那么存儲(chǔ)一個(gè)元素就要跟已存在的所有元素比較一遍敷待,比如已存入100個(gè)元素,那么存101個(gè)元素的時(shí)候,就要調(diào)用equals方法100次思劳。

但如果用hashcode()方法的話潜叛,他就利用了hash算法來(lái)存儲(chǔ)數(shù)據(jù)的。

這樣的話每存一個(gè)數(shù)據(jù)就調(diào)用一次hashcode()方法,得到一個(gè)hashcode值及存入的位置牡属。如果該位置不存在數(shù)據(jù)那么就直接存入逮栅,否則調(diào)用一次equals()方法措伐,不相同則存侥加,相同不存担败。這樣下來(lái)整個(gè)存儲(chǔ)下來(lái)不需要調(diào)用幾次equals方法,雖然多了幾次hashcode方法,但相對(duì)于前面來(lái)講效率高了不少狈网。

4 為什么要重寫(xiě)equals方法

因?yàn)镺bject的equal方法默認(rèn)是兩個(gè)對(duì)象的引用的比較勇垛,意思就是指向同一內(nèi)存,地址則相等士鸥,否則不相等础淤;如果你現(xiàn)在需要利用對(duì)象里面的值來(lái)判斷是否相等,則重載equal方法。

說(shuō)道這個(gè)地方我相信很多人會(huì)有疑問(wèn)玻侥,相信大家都被String對(duì)象的equals()方法和"=="糾結(jié)過(guò)一段時(shí)間掌桩,當(dāng)時(shí)我們知道String對(duì)象中equals方法是判斷值的,而==是地址判斷茅坛。

那照這么說(shuō)equals怎么會(huì)是地址的比較呢则拷?

那是因?yàn)閷?shí)際上JDK中斥铺,String晾蜘、Math等封裝類都對(duì)Object中的equals()方法進(jìn)行了重寫(xiě)眠屎。

我們先看看Object中equals方法的源碼:

public boolean equals(Object obj) { 
         return (this == obj); 
}

我們都知道所有的對(duì)象都擁有標(biāo)識(shí)(內(nèi)存地址)和狀態(tài)(數(shù)據(jù))组力,同時(shí)“==”比較兩個(gè)對(duì)象的的內(nèi)存地址燎字,所以說(shuō)使用Object的equals()方法是比較兩個(gè)對(duì)象的內(nèi)存地址是否相等候衍,即若object1.equals(object2)為true蛉鹿,則表示equals1和equals2實(shí)際上是引用同一個(gè)對(duì)象往湿。雖然有時(shí)候Object的equals()方法可以滿足我們一些基本的要求领追,但是我們必須要清楚我們很大部分時(shí)間都是進(jìn)行兩個(gè)對(duì)象的比較棕孙,這個(gè)時(shí)候Object的equals()方法就不可以了,所以才會(huì)有String這些類對(duì)equals方法的改寫(xiě)蟀俊,依次類推Double矛洞、Integer缚甩、Math等等窑邦。這些類都是重寫(xiě)了equals()方法的冈钦,從而進(jìn)行的是內(nèi)容的比較瞧筛。希望大家不要搞混了较幌。

5 改寫(xiě)equals時(shí)總是要改寫(xiě)hashcode

java.lnag.Object中對(duì)hashCode的約定:

  1. 在一個(gè)應(yīng)用程序執(zhí)行期間乍炉,如果一個(gè)對(duì)象的equals方法做比較所用到的信息沒(méi)有被修改的話,則對(duì)該對(duì)象調(diào)用hashCode方法多次,它必須始終如一地返回同一個(gè)整數(shù)槐瑞。
  2. 如果兩個(gè)對(duì)象根據(jù)equals(Object o)方法是相等的祠挫,則調(diào)用這兩個(gè)對(duì)象中任一對(duì)象的hashCode方法必須產(chǎn)生相同的整數(shù)結(jié)果悼沿。
  3. 如果兩個(gè)對(duì)象根據(jù)equals(Object o)方法是不相等的显沈,則調(diào)用這兩個(gè)對(duì)象中任一個(gè)對(duì)象的hashCode方法,不要求產(chǎn)生不同的整數(shù)結(jié)果鳖藕。但如果能不同著恩,則可能提高散列表的性能蜻展。

根據(jù)上一個(gè)問(wèn)題伍茄,實(shí)際上我們已經(jīng)能很簡(jiǎn)單的解釋這一點(diǎn)了施逾,比如改寫(xiě)String中的equals為基于內(nèi)容上的比較而不是內(nèi)存地址的話汉额,那么雖然equals相等蠕搜,但并不代表內(nèi)存地址相等,由hashcode方法的定義可知內(nèi)存地址不同,沒(méi)改寫(xiě)的hashcode值也可能不同俱萍。所以違背了第二條約定告丢。

又如new一個(gè)對(duì)象岖免,再new一個(gè)內(nèi)容相等的對(duì)象颅湘,調(diào)用equals方法返回的true栗精,但他們的hashcode值不同鹿寨,將兩個(gè)對(duì)象存入HashSet中脚草,會(huì)使得其中包含兩個(gè)相等的對(duì)象,因?yàn)槭窍葯z索hashcode值埂淮,不等的情況下才會(huì)去比較equals方法的倔撞。

6 hashCode方法使用介紹

Hash表數(shù)據(jù)結(jié)構(gòu)常識(shí):
1误窖、哈希表基于數(shù)組秩贰。
2丙唧、缺點(diǎn):基于數(shù)組的想际,數(shù)組創(chuàng)建后難以擴(kuò)展溪厘。某些哈希表被基本填滿時(shí)畸悬,性能下降得非常嚴(yán)重蹋宦。
3冷冗、沒(méi)有一種簡(jiǎn)便得方法可以以任何一種順序遍歷表中數(shù)據(jù)項(xiàng)蒿辙。
4滨巴、如果不需要有序遍歷數(shù)據(jù),并且可以提前預(yù)測(cè)數(shù)據(jù)量的大小绪颖,那么哈希表在速度和易用性方面是無(wú)與倫比的窃款。


參考鏈接

https://blog.csdn.net/pangjiuzala/article/details/48194549
https://www.cnblogs.com/jesonjason/p/5492208.html
https://www.cnblogs.com/Eason-S/p/5524837.html
https://blog.csdn.net/tcytcy123/article/details/50836323

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搬俊,隨后出現(xiàn)的幾起案子餐屎,更是在濱河造成了極大的恐慌玩祟,老刑警劉巖空扎,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盘寡,死亡現(xiàn)場(chǎng)離奇詭異撮慨,居然都是意外死亡甫煞,警方通過(guò)查閱死者的電腦和手機(jī)抚吠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門楷力,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)岔留,“玉大人献联,你說(shuō)我怎么就攤上這事何址∮米Γ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵颇玷,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我磁餐,道長(zhǎng)诊霹,這世上最難降的妖魔是什么脾还? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮怔蚌,結(jié)果婚禮上桦踊,老公的妹妹穿的比我還像新娘终畅。我一直安慰自己,他們只是感情好杖狼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布理朋。 她就那樣靜靜地躺著嗽上,像睡著了一般斜友。 火紅的嫁衣襯著肌膚如雪鲜屏。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天也殖,我揣著相機(jī)與錄音,去河邊找鬼己儒。 笑死捆毫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绩卤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼濒憋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了凛驮?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捂贿,沒(méi)想到半個(gè)月后纠修,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扣草,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年辰妙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片密浑。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖尔破,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情懒构,我是刑警寧澤餐济,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布胆剧,位于F島的核電站,受9級(jí)特大地震影響秩霍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜铃绒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匿垄。 院中可真熱鬧移宅,春花似錦椿疗、人聲如沸漏峰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至靖苇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贤壁,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工脾拆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人名船。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像渠驼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子迷扇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容