聊聊Java中的 " == "誓琼、equals以及hashCode

關(guān)于 “ == ”

“ == ”操作符主要比較的是操作符兩端對象的內(nèi)存地址腹侣。如果兩個對象的內(nèi)存地址是一致的,那么就返回true今穿。反之伦籍,則返回false腮出。

話不多說胚嘲,先來看看下面這段代碼:

程序1-1

再來看看會輸出什么:

上面程序的輸出結(jié)果

怎么樣,這個結(jié)果你想到了嗎攻锰?

以下是關(guān)于上面結(jié)果的解釋:

其實在Java中對于字符串的創(chuàng)建娶吞,有兩種形式。

  • 一種是形如String a = "Hello"這樣的字面量形式妒蛇;
  • 另一種是形如String d = new String("Hello")這樣的構(gòu)造對象的方法绣夺。
字面量形式

在JVM中,為了減少對字符串變量的重復(fù)創(chuàng)建奋蔚,其擁有一段特殊的內(nèi)存空間烈钞,這段內(nèi)存被稱為字符串常量池(constant pool)或者是“字符串字面量池”棵磷。

程序1-1中,我們首先使用 String a = "Hello" 創(chuàng)建了一個對象沉桌。此時默認字符串常量池中沒有內(nèi)容為“Hello”的對象存在算吩。當JVM在字符串常量池中沒有找到內(nèi)容為"Hello"的對象時偎巢,就會創(chuàng)建一個內(nèi)容為"Hello"的對象,并將它的引用返回給變量a求冷。

那么當我們在后面使用 String b = "Hello" 的時候窍霞,會發(fā)生什么情況呢但金?
首先JVM會在字符串常量池中查詢是否有內(nèi)容為“Hello”的對象。因為此時字符串常量池中已經(jīng)有內(nèi)容為“Hello”的對象了钱磅,故JVM不會創(chuàng)建新的地址空間,而是將原有的“Hello”對象的引用返回給b------所以此時變量a和變量b擁有的是同一個對象引用年柠,即指向的是同一塊內(nèi)存地址彪杉,因此使用 == 操作符對a和b進行比較牵咙,結(jié)果為true洁桌。

使用new來構(gòu)造一個對象

然而當我們new一個字符串對象時,不管字符串常量池中是否有內(nèi)容相同的對象谱轨,JVM都會創(chuàng)造一個新的對象吠谢,所以地址肯定不一樣工坊,因此使用 == 操作符對a和d進行比較,結(jié)果為false罢吃。

關(guān)于equals

在Object類中昭齐,equals方法源碼如下:

Object類中的equals方法

我們可以看到阱驾,在Object類中的equals方法其實在內(nèi)部也是使用了“ == ”操作符-----如果兩個對象地址一樣則返回true里覆,反之則返回false。
既然equals方法里面也是使用“ == ”,那為什么還要設(shè)立一個equals方法割去,而不是直接用“ == ”操作符呢?

別急夸赫,這只是Object類里面的equals方法咖城,其實絕大部分Object的子類都對equals方法進行了改寫:

String類中的equals方法
HashMap中的equals方法

除了會比較內(nèi)存地址切平,以上兩個類中的equals方法還會比較對象所包含的內(nèi)容辐董。如果兩個對象所包含的內(nèi)容相同,equals方法也是返回true苔严。

官方文檔中對equals方法的描述:

equals方法在非空對象引用上實現(xiàn)相等關(guān)系:

  • 自反性:對于任何非空引用值x届氢,x.equals(x)都應(yīng)返回true覆旭。
  • 對稱性:對于任何非空引用值x和y姐扮,當且僅當y.equals(x)返回true時,x.equals(y)才應(yīng)返回true壤靶。
  • 傳遞性:對于任何非空引用值x惊搏、y和z恬惯,如果x.equals(y)返回true,并且y.equals(z)返回true浓恳,那么x.equals(z)應(yīng)返回true颈将。
  • 一致性:對于任何非空引用值x和y,多次調(diào)用x.equals(y)始終返回true
    或始終返回false颂砸,前提是對象上equals比較中所用的信息沒有被修改死姚。對于任何非空引用值x都毒,x.equals(null)都應(yīng)返回false。
    Object類的equals方法實現(xiàn)對象上差別可能性最大的相等關(guān)系保屯;即姑尺,對于任何非空引用值x和y蝠猬,當且僅當x和y引用同一個對象時榆芦,此方法才返回true(x == y具有值true)。
    注意:當此方法被重寫時驻右,通常有必要重寫hashCode方法崎淳,以維護hashCode方法的常規(guī)協(xié)定拣凹,該協(xié)定聲明相等對象必須具有相等的哈希碼。

其實在很多時候爬迟,我們都需要改寫equals方法以適應(yīng)我們的實際情況:

下面是一個Person類付呕,包含name和age兩個屬性。

Person類

現(xiàn)在我們創(chuàng)建兩個Person對象棒搜,但是name和age分別都設(shè)置一樣的值,同時使用equals方法對這兩個對象進行比較:

創(chuàng)建兩個Person對象并用equals方法對其進行比較
比較之后的結(jié)果

雖然這從JVM的角度來看這個程序是對的育韩,可是結(jié)果并不是我們想要的:對象one和對象two雖然是兩個不同的對象筋讨,但是它們包含的元素的值是相同的摸恍,也就是說one和two應(yīng)該都表示的是同一個人-----name 為 EakonZhao立镶,年齡為19∈嚷撸可是為什么調(diào)用equals方法之后輸出的卻是false呢栈顷?

原因:由于我們沒有對equals方法進行改寫嵌巷,所以當我們在調(diào)用equals方法的時候?qū)嶋H上調(diào)用的是Object類的equals方法搪哪。從前面我們可以得知,Object的equals方法在內(nèi)部是直接使用“ == ”操作符對對象進行比較颤难,這樣當然會返回false啦行嗤!

下面我將對equals方法進行改寫垛耳,以滿足我們的需求;

改寫之后的equals方法
改寫equals方法之后的輸出結(jié)果

對于改寫equals方法之后是否需要改寫hashCode方法以維護hashCode方法的常規(guī)協(xié)定栈雳,我在介紹完hashCode方法之后會繼續(xù)講

關(guān)于在使用"=="操作符和equals方法時發(fā)生的自動裝箱與自動拆箱現(xiàn)象

簡單介紹一下自動拆箱和自動裝箱

自動裝箱:將Java的基本類型轉(zhuǎn)換成包裝器類型;
自動拆箱:將Java的包裝器類型轉(zhuǎn)換成基本類型霉旗。

基本數(shù)據(jù)類型及其包裝器類型
自動裝箱與自動拆箱

如上圖所示厌秒,其實這是一個非常簡單的程序鸵闪,但是實際上Java自動幫我們完成了拆裝箱的操作暑诸。

背景:
在JDK1.5之前个榕,如果我們要生成一個數(shù)值為1的Integer對象,那么我們必須使用下面這行代碼:

創(chuàng)建一個數(shù)值為1的Integer對象

可是引入了自動裝箱功能之后夏志,我們只需使用這樣一行代碼就能完成了:

自動裝箱

這是如何實現(xiàn)的呢沟蔑?下面我們將上面那段代碼的class文件反編譯一下:

反編譯之后的代碼

我們可以看到瘦材,其實Java是使用了 Integer的valueOf(int)方法來完成自動裝箱的食棕,而在拆箱過程當中調(diào)用的是Integer的intValue方法错沽。對于其他的包裝器類型來說千埃,其實這兩個過程也是類似的。

裝箱過程是通過調(diào)用包裝器類型的valueOf方法實現(xiàn)的谒臼,拆箱過程是通過調(diào)用包裝器類型的xxValue方法實現(xiàn)的(其中xx代表的是對應(yīng)的基本類型)蜈缤。

讓我們再來看看下面這段代碼:

Integer緩存示例

為什么上面兩條打印代碼會輸出不同的結(jié)果底哥?
原因也很簡單:與字符串常量池類似,這其實也是JVM節(jié)省內(nèi)存的一個方法-----對于Integer類型的對象來說奶陈,如果我們要創(chuàng)建的Integer對象的數(shù)值在 [-128,127]的區(qū)間之內(nèi)附较,那么JVM就會在緩存中查找拒课,看看有沒有已經(jīng)存放在緩存中的數(shù)值一樣的Integer對象事示。如果有肖爵,就返回已經(jīng)存在的對象的引用。

關(guān)于使用equals方法時發(fā)生的自動拆裝箱現(xiàn)象就不贅述了冀自,其實很容易理解熬粗。

小結(jié): “==”運算符其實比較的是地址相不相同余境,而equals方法比較的是值相不相同芳来。

關(guān)于hashCode

官方文檔中隊hashCode方法的描述:

public int hashCode()
返回該對象的哈希碼值。支持此方法是為了提高哈希表(例如java.util.Hashtable
提供的哈希表)的性能佣盒。
hashCode的常規(guī)協(xié)定是:

  • 在 Java 應(yīng)用程序執(zhí)行期間沼撕,在對同一對象多次調(diào)用hashCode方法時,必須一致地返回相同的整數(shù)磨总,前提是將對象進行equals比較時所用的信息沒有被修改蚪燕。從某一應(yīng)用程序的一次執(zhí)行到同一應(yīng)用程序的另一次執(zhí)行奔浅,該整數(shù)無需保持一致汹桦。
  • 如果根據(jù)equals(Object)方法,兩個對象是相等的钥弯,那么對這兩個對象中的每個對象調(diào)用hashCode方法都必須生成相同的整數(shù)結(jié)果督禽。
  • 如果根據(jù)equals(java.lang.Object)方法狈惫,兩個對象不相等,那么對這兩個對象中的任一對象上調(diào)用hashCode方法要求一定生成不同的整數(shù)結(jié)果忆肾。但是难菌,程序員應(yīng)該意識到蔑滓,為不相等的對象生成不同整數(shù)結(jié)果可以提高哈希表的性能键袱。

實際上蹄咖,由Object類定義的 hashCode 方法確實會針對不同的對象返回不同的整數(shù)。(這一般是通過將該對象的內(nèi)部地址轉(zhuǎn)換成一個整數(shù)來實現(xiàn)的蚜迅,但是 JavaTM
編程語言不需要這種實現(xiàn)技巧谁不。)

上面這段話,簡單的來說就是以下幾點:

  • hashCode存在的意義主要是提供查找的快捷性吵血,比如說在Hashtable蹋辅、HashMap中等挫掏。hashCode是用來在散列存儲結(jié)構(gòu)中確定對象存儲的位置的;
  • 如果兩個對象相同尉共,即調(diào)用equals方法返回的是true,那么它倆的hashCode值也要相同;
  • 如果equals方法被改寫了,那么hashCode方法也盡量要改寫杠河,并且產(chǎn)生hashCode的對象也要和equals的對象保持一致券敌;
  • 兩個對象的hashCode相同并不代表兩個對象就一定相同柳洋,也就是不一定適用于equals(java.lang.Object)方法熊镣,只能夠說明這兩個對象在散列存儲對象中绪囱,如Hashtable中,是存放在同一個籃子里的扣甲。(關(guān)于Hashtable的介紹我在后面會開博客探究其源碼)

下面這段話是從別人那里轉(zhuǎn)過來的琉挖,我覺得能幫助理解hashCode:

  • hashcode是用來查找的示辈,如果你學(xué)過數(shù)據(jù)結(jié)構(gòu)就應(yīng)該知道,在查找和排序這一章有
    例如內(nèi)存中有這樣的位置
    01234567
    而我有個類坠敷,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一膝迎,如果不用hashcode而任意存放胰耗,那么當查找時就需要到這八個位置里挨個去找柴灯,或者用二分法一類的算法赠群。
    但如果用hashcode那就會使效率提高很多。
    我們這個類中有個字段叫ID,那么我們就定義我們的hashcode為ID%8突委,然后把我們的類存放在取得得余數(shù)那個位置匀油。比如我們的ID為9勾笆,9除8的余數(shù)為1窝爪,那么我們就把該類存在1這個位置,如果ID是13帅韧,求得的余數(shù)是5忽舟,那么我們就把該類放在5這個位置叮阅。這樣,以后在查找該類時就可以通過ID除8求余數(shù)直接找到存放的位置了挑随。
  • 但是如果兩個類有相同的hashcode怎么辦呢兜挨?(我們假設(shè)上面的類的ID不是唯一的),例如9除以8和17除以8的余數(shù)都是1眯分,那么這是不是合法的拌汇,回答是:可以這樣。那么如何判斷呢弊决?在這個時候就需要定義equals了噪舀。
    也就是說,我們先通過hashcode來判斷兩個類是否存放某個桶里飘诗,但這個桶里可能有很多類与倡,那么我們就需要再通過equals來在這個桶里找到我們要的類。
    那么昆稿。重寫了equals(),為什么還要重寫hashCode()呢溉潭?
    想想净响,你要在一個桶里找東西,你必須先要找到這個桶啊岛抄,你不通過重寫hashcode()來找到桶别惦,光重寫equals()有什么用啊

下面是代碼示例:
我新建了一個HashExample類狈茉,里面定義了一個屬性為id夫椭,并改寫了hashCode方法:

HashExample類

現(xiàn)在我new兩個對象,這兩個對象的id我都賦予相同的值氯庆,并將它們兩個存入一個Set(Set中的元素是不重復(fù)的)當中蹭秋,然后分別輸出它們兩個的hashCode以及使用equals方法比較的結(jié)果以及將這個Set也輸出:

改寫equals方法之前

以上這個示例,我們只是重寫了hashCode方法堤撵,從上面的結(jié)果可以看出仁讨,雖然兩個對象的hashCode相等,但是實際上兩個對象并不是相等实昨;洞豁,我們沒有重寫equals方法,那么就會調(diào)用object默認的equals方法,是比較兩個對象的引用是不是相同丈挟,顯示這是兩個不同的對象刁卜,兩個對象的引用肯定是不定的。這里我們將生成的對象放到了hashSet中曙咽,而hashSet中只能夠存放唯一的對象沛善,也就是相同的(適用于equals方法)的對象只會存放一個鲁纠,但是這里實際上是兩個對象a,b都被放到了HashSet中,這樣hashSet就失去了他本身的意義了。

現(xiàn)在把equals也改寫一下:

改寫之后的equals方法
改寫equals方法之后的輸出結(jié)果

現(xiàn)在我們可以看到醇锚,這兩個對象已經(jīng)完全相等了,并且hashSet中也只存放了一份對象粗俱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜻展,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子烁竭,更是在濱河造成了極大的恐慌菲茬,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件派撕,死亡現(xiàn)場離奇詭異婉弹,居然都是意外死亡,警方通過查閱死者的電腦和手機终吼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門镀赌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人际跪,你說我怎么就攤上這事商佛。” “怎么了姆打?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵良姆,是天一觀的道長。 經(jīng)常有香客問我幔戏,道長玛追,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任闲延,我火速辦了婚禮痊剖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘垒玲。我一直安慰自己陆馁,他們只是感情好,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布合愈。 她就那樣靜靜地躺著叮贩,像睡著了一般击狮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上益老,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天帘不,我揣著相機與錄音,去河邊找鬼杨箭。 笑死寞焙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的互婿。 我是一名探鬼主播捣郊,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼慈参!你這毒婦竟也來了呛牲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤驮配,失蹤者是張志新(化名)和其女友劉穎娘扩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壮锻,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡琐旁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了猜绣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灰殴。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖掰邢,靈堂內(nèi)的尸體忽然破棺而出牺陶,到底是詐尸還是另有隱情,我是刑警寧澤辣之,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布掰伸,位于F島的核電站,受9級特大地震影響怀估,放射性物質(zhì)發(fā)生泄漏狮鸭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一奏夫、第九天 我趴在偏房一處隱蔽的房頂上張望怕篷。 院中可真熱鬧历筝,春花似錦酗昼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒸痹。三九已至,卻和暖如春呛哟,著一層夾襖步出監(jiān)牢的瞬間叠荠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工扫责, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榛鼎,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓鳖孤,卻偏偏與公主長得像者娱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子苏揣,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

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