【JAVA學習】java中==、equals()、hashCode()都和對象的比較有關,在java中這三者各有什麼用處呢,即java中爲什麼需要設計這三種對象的比較方法呢?

  • 前言

    java中==、equals()、hashCode()都和對象的比較有關,在java中這三者各有什麼用處呢,即java中爲什麼需要設計這三種對象的比較方法呢?

  1. 關於==

        ==是容易理解的。java設計java就是要比較兩個對象是不是同一個對象。

        對於引用變量而言,比較的時候兩個引用變量引用的是不是同一個對象,即比較的是兩個引用中存儲的對象地址是不是一樣的。

        對於基本數據類型而言,比較的就是兩個數據是不是相等,沒什麼歧義。

        由於對於基本數據類型而言,沒有方法,所以不存在equal()和hashCode()的問題,下面的討論都是針對引用類型而言的。

  2. 關於equals()

     

    爲什麼java會設計equals()方法?

        ==比較的是兩個對象是否是同一個對象,這並不能滿足很多需求。有時候當兩個對象不==的時候,我們仍然會認爲兩者是“相等”的,比如對於String對象,當兩個對象的字符串序列是一直的,我們就認爲他們是“相等”的。對於這樣的需求,需要equals()來實現。對於有這種需求的對象的類,重寫其equals()方法便可,具體的“相等”邏輯可以根據需要自己定義。

    需要注意的地方

        Object中equals()的默認實現是比較兩個對象是不是==,即其和==的效果是相同的。

       java提供的某些類已經重寫了equals()方法。自己寫的類,如果需要實現自己的“相等”邏輯,需要重寫equals()方法。

       

  3. 關於hashCode()

    爲什麼會設計hashCode()方法?

       hashCode()方法返回的就是一個數值,我們稱之爲hashCode吧。從方法的名稱上就可以看出,其目的是生成一個hash碼。hash碼的主要用途就是在對對象進行散列的時候作爲key輸入,據此很容易推斷出,我們需要每個對象的hash碼儘可能不同,這樣才能保證散列的存取性能。事實上,Object類提供的默認實現確實保證每個對象的hash碼不同(在對象的內存地址基礎上經過特定算法返回一個hash碼)。

        分析到這個地方,看似沒什麼問題,三者的作用很清晰,好像他們之間也沒什麼關係。在java的規範上,hashCode()方法和equals()方法確實可以沒有關係。

        但是!!!!!!!!有一個問題。

        問題如下:對於集合類HashSet、HashMap等和hash有關的類(以HashSet爲例),是通過hash算法來散列對象的。對HashSet而言,存入對象的流程爲:根據對象的hash碼,經過hash算法,找到對象應該存放的位置,如果該位置爲空,則將對象存入該位置;如果該位置不爲空,則使用equals()比較該位置的對象和將要入的對象,如果兩個相等,則不再插入,如果不相等,根據hash衝突解決算法將對象插入其他位置。

       而java規定對於HashSet判斷是不是重複對象就是通過equals() 方法來完成,這就需要在兩個對象equals()方法相等的時候,hash碼一定相等(即hashCode()返回的值相等)。假設兩個對象equals()方法相等的時候,hash碼不相等,會出現equals()相等的兩個對象都插入了HashSet中,這時不允許的。從而我們有了一下的結論:

        結論:對於equals()相等的兩個對象,其hashCode()返回的值一定相等

       通過上面的分析,對於這個結論是沒有異議的。結合前面關於hash碼要儘可能不同的要求,現在變成了對於equals()相等的對象hash碼要一定相等,而對於equals()不同的對象要儘量做到hash碼不同。那麼怎麼才能保證這一點呢?

  4. 重寫hashCode()

    首先,如何保證“對於equals()相等的對象hash碼要一定相等”。

        equals()方法中對於對象的比較是通過比較對象中全部或者部分字段來完成的,這些字段集合記爲集合A,如果我們計算hash碼的時候,如果只是從集合A中選取部分字段或者全部字段來完成便可,因爲輸入相同,不管經過什麼算法,輸出一定相同(在方法中調用隨機函數?這屬於吃飽了撐的!)。如此設計便保證滿足了第一個要求。

    其次,對於equals()不同的對象要儘量做到hash碼不同。

        對於這一點的保證就是在設計一個好的算法,讓不同的輸入儘可能產生不同的輸出。

       下面就詳細介紹一下如何設計這個算法。這個算法是有現成的參考的,算法的具體步驟就是:

    [1]把某個非零常數值(一般取素數),例如17,保存在int變量result中;

    [2]對於對象中每一個關鍵域f(指equals方法中考慮的每一個域):

       [2.1]boolean型,計算(f ? 0 : 1);

       [2.2]byte,char,short型,計算(int)f;

       [2.3]long型,計算(int) (f ^ (f>>>32));

       [2.4]float型,計算Float.floatToIntBits(afloat);

       [2.5]double型,計算Double.doubleToLongBits(adouble)得到一個long,再執行[2.3];

       [2.6]對象引用,遞歸調用它的hashCode方法;

       [2.7]數組域,對其中每個元素調用它的hashCode方法。

    [3]將上面計算得到的散列碼保存到int變量c,然後執行 result=37*result+c;

    [4]返回result。

    其實其思路就是:先去一個基數,然後對於equals()中考慮的每一個域,先轉換成整數,再執行result=37*result+c;

 

尊重版權,轉自百度經驗:http://jingyan.baidu.com/article/ff41162582507512e5823763.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章