Java功底篇系列-01-從==/equals/hashCode開始

來源:互聯網
上載者:User

標籤:java equals == hashcode

前言

自己畢業後做JAVA開發已經2年了,從最初的JAVA WEB開發,到投入到Hadoop大資料開發潮流中,越來越發現自己的JAVA基礎,資料結構,多線程等等,是那麼的脆弱!還有什麼比基礎更加重要呢,應該靜下心來,去學習它們,就從這一篇部落格開始吧~


話題一:==與equals


讓我們首先看看Object類中定義的equals方法的源碼:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/71/1B/wKioL1XGpUeBnt2JAAAzIok0Uuo792.jpg" title="搜狗_2015-08-09_08-47-57.png" alt="wKioL1XGpUeBnt2JAAAzIok0Uuo792.jpg" />


在Object類中,equals就是利用==實現對象之間的比較的。


我們知道對於基礎資料型別 (Elementary Data Type),==比較的就是他們的值是否相等,那麼對於物件類型的==比較的是什麼呢?


對於物件類型,a==b比較的就是a和b是否就是同一個對象!


很顯然,我們常常需要做的就是比較2個對象是不是從業務的角度相等,而並不是想比較他們是不是同一個對象。因此我們需要override equals()方法。


比如String就是有這樣的需求,因此我們來看看她是怎麼複寫equals()的:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/71/1F/wKiom1XGpROR4gGHAAE-avv4FK0770.jpg" title="搜狗_2015-08-09_08-55-36.png" alt="wKiom1XGpROR4gGHAAE-avv4FK0770.jpg" />


String的obj1.equals(obj2)實現的步驟:


第一,利用==看一下,obj1和obj2是不是同一個對象,如果是那麼則肯定equals的!


第二,他們的類型相同嗎?長度相同嗎?


第三,開始逐個字元進行==比較,如果出現不同,則可以得到結果,否則繼續比較。


感悟:


以後,我們去實現自訂類的equals方法時,是不是要考慮下:


他們是同一個對象嗎?


他們是同一個資料類型嗎?


他們自身有哪些關鍵屬性,是否通過比較這些關鍵屬性來快速得到結論呢?




話題二:equals與hashCode


hashCode(),和equals()一樣,都是在Object中定義的,我們來看看吧:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/71/1B/wKioL1XGqsvShSNTAAAbVHQbwxw333.jpg" title="搜狗_2015-08-09_09-11-31.png" alt="wKioL1XGqsvShSNTAAAbVHQbwxw333.jpg" />


很不幸,在Object中他是一個native調用,我們還是看看String中的hashCode是如何是實現的:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/71/1F/wKiom1XGqXSAxWzcAACu_Bl9a64965.jpg" title="搜狗_2015-08-09_09-14-19.png" alt="wKiom1XGqXSAxWzcAACu_Bl9a64965.jpg" />


分析:


第一,可以看出,一旦一個String在調用hashCode方法後,那麼hash屬性就會得到一次賦值,那麼以後再次調用這個hashCode方法,就會直接返回hash屬性,而不再計算了。


第二,從hashCode方法內部的for迴圈可以看出,這個hash值,是一個綜合值,是綜合了每一個字元計算得到的。


想法:


如果2個String他們的字元內容完全一樣,那麼顯然他們的hashCode理應一樣!


如果2個String他們的字元內容不同,那麼hashCode就不同嗎?根據上面的分析,hashCode是一個綜合值,字元內容不同,完全可以得到一樣的hashCode!


那如果hashCode一樣,那也不到任何結論!


那如果hashCode不一樣,那麼對於String而言,他們的字元內容肯定不同!



關係:


String的字元內容的比較正是equals方法,那麼equals與hashCode是什麼關係呢?


以前,我以為自訂一個類,就應該重寫這2個方法,他們應該是“天生一對”。其實不是!


上面的話題,其實已經說明,我們重寫equals方法是為了從業務角度來實現2個對象的比較,那hashCode是幹嘛的呢?


我們知道演算法的基礎是數字,比如hash演算法。在map中,很顯然如果KEY是一個對象的話,那麼這個對象應該實現HASH的尋找和寫入,可是他是一個對象啊,他應該提供一個數字,以便於實現HASH演算法!


也就是說,hashCode是為了給對象一個數字,這個數字用於HASH這種資料結構中,去實現快速定位對象用的!hashCode定位鏈表後,其實還是得迴圈鏈表利用equals進行比較。


那equals和hashCode其實沒有什麼關係,不是非要在一起的,一個是為了比較對象是不是業務上相等,一個是為了HASH快速定位而存在的。



話題三:String的==迷惑


簡單看一段代碼吧:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/71/1F/wKiom1XGtKTxITK5AACRhBmhr74708.jpg" title="搜狗_2015-08-09_10-02-01.png" alt="wKiom1XGtKTxITK5AACRhBmhr74708.jpg" />


結果是:


true

false


對於String而言,equals我們犯不了什麼錯誤,可是==是經常讓我們迷惑的地方!


第一,String在編譯期存在最佳化行為


例如,String s = "a" + "b"; 在編譯產生的CLASS檔案中已經是這樣的了:


String s = "ab";


那麼上面的代碼中,s4會在編譯期就變成"abc"嗎?不會,因為s2是一個變數,只能在運行期間確定,而且並沒有強制限制式他不能變。而s5在編譯期間就已經確定為"abc",因為s3是final的,變不了的!


是這樣嗎?


我們來利用反組譯碼命令javap命令看下:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/71/1F/wKiom1XGufqQOkrsAAMBToUUQkA377.jpg" title="搜狗_2015-08-09_10-24-46.png" alt="wKiom1XGufqQOkrsAAMBToUUQkA377.jpg" />


很多指令,我也不懂,我們只看能看得懂的地方,或者猜一猜~


從0到8,根據注釋String ab,應該是對應於代碼的


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/71/1B/wKioL1XGvLPif8HqAAA3xNvyUos267.jpg" title="搜狗_2015-08-09_10-27-51.png" alt="wKioL1XGvLPif8HqAAA3xNvyUos267.jpg" />


從而也驗證了,String存在編譯期合并的事實。



從9到28,發生了new 操作,還有append,這應該對應於:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M01/71/1F/wKiom1XGuyeDPjpxAAAWR85yXxU835.jpg" title="搜狗_2015-08-09_10-29-49.png" alt="wKiom1XGuyeDPjpxAAAWR85yXxU835.jpg" />


這說明,對於String而言,變數與常量的這種“+”操作,實際上在底層是搞成了一個StringBuilder進行append,最後在來一個toString得到的!


從30到32,根據注釋String abc,應該對應於代碼:


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M00/71/1C/wKioL1XGvhzwTwYqAAARxH8m2Ec650.jpg" title="搜狗_2015-08-09_10-33-57.png" alt="wKioL1XGvhzwTwYqAAARxH8m2Ec650.jpg" />


這也說明了,final變數與常量的這種“+”操作,直接編譯合并!



第二,String存在常量池的概念


在編譯期間就確定的String常量,是存在String pool中的,而且是保證全域唯一的。也就是說上面代碼中,s1/s2指向String Pool中的同一個string。


相信到這裡以後String的==以後不會在迷糊了,呵呵~



話題四:String +  VS  StringBuffer.append  誰更快?


我們都說,StringBuffer.append好,她更快?


根據話題三的分析,String的“+”可能在編譯期間就合并了,而StringBuffer.append是必須在運行期間進行的操作,很顯然,從這種角度而言,應該String的“+”更快些!


那麼以前是怎麼回事呢?


650) this.width=650;" src="http://s3.51cto.com/wyfs02/M02/71/1F/wKiom1XGwMXhOFzEAACC08FRUZQ515.jpg" title="搜狗_2015-08-09_10-53-47.png" alt="wKiom1XGwMXhOFzEAACC08FRUZQ515.jpg" />


對於第一個for迴圈而言,注意到

s += i;

根據上面的分析,這是要發生new StringBuilder,append,toString操作的。

也就是,我們在一個大大的for迴圈中進行了new操作!真是不可思議!這並不是我們預料的!

我們暫且不考慮StringBuilder擴容機制,要知道隨著迴圈的進行,字串內容越來越大,new產生的垃圾對象也越來越大,可能記憶體不足了,GC要開始忙碌了!這應該就是程式效能下降的原因。


而對於第二個for迴圈而言,

沒有new操作,雖然涉及到擴容,但是沒有那麼多的垃圾。



這篇部落格就到這裡了,收穫很多,希望對大家也有協助~


本文出自 “努力奮鬥” 部落格,請務必保留此出處http://zhangfengzhe.blog.51cto.com/8855103/1683059

Java功底篇系列-01-從==/equals/hashCode開始

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.