乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      一次性搞清楚equals和hashCode

       Levy_X 2017-05-26

      前言


      在程序設(shè)計(jì)中,有很多的“公約”,遵守約定去實(shí)現(xiàn)你的代碼,會(huì)讓你避開很多坑,這些公約是前人總結(jié)出來的設(shè)計(jì)規(guī)范。


      Object類是Java中的萬類之祖,其中,equals和hashCode是2個(gè)非常重要的方法。


      這2個(gè)方法總是被人放在一起討論。最近在看集合框架,為了打基礎(chǔ),就決定把一些細(xì)枝末節(jié)清理掉。一次性搞清楚!


      下面開始剖析。


       


      public boolean equals(Object obj)


       


      Object類中默認(rèn)的實(shí)現(xiàn)方式是  :   return this == obj  。那就是說,只有this 和 obj引用同一個(gè)對(duì)象,才會(huì)返回true。


      而我們往往需要用equals來判斷 2個(gè)對(duì)象是否等價(jià),而非驗(yàn)證他們的唯一性。這樣我們?cè)趯?shí)現(xiàn)自己的類時(shí),就要重寫equals.


       


      按照約定,equals要滿足以下規(guī)則。


       


      自反性:  x.equals(x) 一定是true


      對(duì)null:  x.equals(null) 一定是false


      對(duì)稱性:  x.equals(y)  和  y.equals(x)結(jié)果一致


      傳遞性:  a 和 b equals , b 和 c  equals,那么 a 和 c也一定equals。


      一致性:  在某個(gè)運(yùn)行時(shí)期間,2個(gè)對(duì)象的狀態(tài)的改變不會(huì)不影響equals的決策結(jié)果,那么,在這個(gè)運(yùn)行時(shí)期間,無論調(diào)用多少次equals,都返回相同的結(jié)果。


       


       一個(gè)例子


      復(fù)制代碼

       1 class Test
       2 {
       3     private int num;
       4     private String data;
       5 
       6     public boolean equals(Object obj)
       7     {
       8         if (this == obj)
       9             return true;
      10 
      11         if ((obj == null) || (obj.getClass() != this.getClass()))
      12             return false;
      13 
                 //能執(zhí)行到這里,說明obj和this同類且非null。
      14         Test test = (Test) obj;
      15         return num == test.num&& (data == test.data || (data != null && data.equals(test.data)));
      16     }
      17 
      18     public int hashCode()
      19     {
      20         //重寫equals,也必須重寫hashCode。具體后面介紹。
      24     }
      25 
      26 }

      復(fù)制代碼

       


       


       


      equals編寫指導(dǎo)


      Test類對(duì)象有2個(gè)字段,num和data,這2個(gè)字段代表了對(duì)象的狀態(tài),他們也用在equals方法中作為評(píng)判的依據(jù)。


      在第8行,傳入的比較對(duì)象的引用和this做比較,這樣做是為了 save time ,節(jié)約執(zhí)行時(shí)間,如果this 和 obj是 對(duì)同一個(gè)堆對(duì)象的引用,那么,他們一定是qeuals 的。

      接著,判斷obj是不是為null,如果為null,一定不equals,因?yàn)榧热划?dāng)前對(duì)象this能調(diào)用equals方法,那么它一定不是null,非null 和 null當(dāng)然不等價(jià)。

      然后,比較2個(gè)對(duì)象的運(yùn)行時(shí)類,是否為同一個(gè)類。不是同一個(gè)類,則不equals。getClass返回的是 this 和obj的運(yùn)行時(shí)類的引用。如果他們屬于同一個(gè)類,則返回的是同一個(gè)運(yùn)行時(shí)類的引用。注意,一個(gè)類也是一個(gè)對(duì)象。




      1、有些程序員使用下面的第二種寫法替代第一種比較運(yùn)行時(shí)類的寫法。應(yīng)該避免這樣做。


       


      復(fù)制代碼

      if((obj == null) || (obj.getClass() != this.getClass())) 

      return false; if(!(obj instanceof Test))

      return false; // avoid 避免!

      復(fù)制代碼

       


       


      它違反了公約中的對(duì)稱原則。



      例如:假設(shè)Dog擴(kuò)展了Aminal類。


      dog instanceof Animal      得到true


      animal instanceof Dog      得到false


       


      這就會(huì)導(dǎo)致


      animal.equls(dog) 返回true
      dog.equals(animal) 返回false

      僅當(dāng)Test類沒有子類的時(shí)候,這樣做才能保證是正確的。


       



      2、按照第一種方法實(shí)現(xiàn),那么equals只能比較同一個(gè)類的對(duì)象,不同類對(duì)象永遠(yuǎn)是false。但這并不是強(qiáng)制要求的。一般我們也很少需要在不同的類之間使用equals。


      3、在具體比較對(duì)象的字段的時(shí)候,對(duì)于基本值類型的字段,直接用 == 來比較(注意浮點(diǎn)數(shù)的比較,這是一個(gè)坑)對(duì)于引用類型的字段,你可以調(diào)用他們的equals,當(dāng)然,你也需要處理字段為null 的情況。對(duì)于浮點(diǎn)數(shù)的比較,我在看Arrays.binarySearch的源代碼時(shí),發(fā)現(xiàn)了如下對(duì)于浮點(diǎn)數(shù)的比較的技巧: 



      if ( Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2) ) //d1 和 d2 是double類型
      
      if(  Float.floatToIntBits(f1) == Float.floatToIntBits(f2)  )      //f1 和 f2 是d2是float類型


       


       


      4、并不總是要將對(duì)象的所有字段來作為equals 的評(píng)判依據(jù),那取決于你的業(yè)務(wù)要求。比如你要做一個(gè)家電功率統(tǒng)計(jì)系統(tǒng),如果2個(gè)家電的功率一樣,那就有足夠的依據(jù)認(rèn)為這2個(gè)家電對(duì)象等價(jià)了,至少在你這個(gè)業(yè)務(wù)邏輯背景下是等價(jià)的,并不關(guān)心他們的價(jià)錢啊,品牌啊,大小等其他參數(shù)。

      5、最后需要注意的是,equals 方法的參數(shù)類型是Object,不要寫錯(cuò)!


       


       


       


       


      public int hashCode()



        這個(gè)方法返回對(duì)象的散列碼,返回值是int類型的散列碼。
      對(duì)象的散列碼是為了更好的支持基于哈希機(jī)制的Java集合類,例如 Hashtable, HashMap, HashSet 等。

       
        關(guān)于hashCode方法,一致的約定是:
       


      重寫了euqls方法的對(duì)象必須同時(shí)重寫hashCode()方法。


      如果2個(gè)對(duì)象通過equals調(diào)用后返回是true,那么這個(gè)2個(gè)對(duì)象的hashCode方法也必須返回同樣的int型散列碼

      如果2個(gè)對(duì)象通過equals返回false,他們的hashCode返回的值允許相同。(然而,程序員必須意識(shí)到,hashCode返回獨(dú)一無二的散列碼,會(huì)讓存儲(chǔ)這個(gè)對(duì)象的hashtables更好地工作。)


       


      在上面的例子中,Test類對(duì)象有2個(gè)字段,num和data,這2個(gè)字段代表了對(duì)象的狀態(tài),他們也用在equals方法中作為評(píng)判的依據(jù)。那么, 在hashCode方法中,這2個(gè)字段也要參與hash值的運(yùn)算,作為hash運(yùn)算的中間參數(shù)。這點(diǎn)很關(guān)鍵,這是為了遵守:2個(gè)對(duì)象equals,那么 hashCode一定相同規(guī)則。


      也是說,參與equals函數(shù)的字段,也必須都參與hashCode 的計(jì)算。


       


       
        合乎情理的是:同一個(gè)類中的不同對(duì)象返回不同的散列碼。典型的方式就是根據(jù)對(duì)象的地址來轉(zhuǎn)換為此對(duì)象的散列碼,但是這種方式對(duì)于Java來說并不是唯一的要求的
        的實(shí)現(xiàn)方式。通常也不是最好的實(shí)現(xiàn)方式。

        相比 于 equals公認(rèn)實(shí)現(xiàn)約定,hashCode的公約要求是很容易理解的。有2個(gè)重點(diǎn)是hashCode方法必須遵守的。約定的第3點(diǎn),其實(shí)就是第2點(diǎn)的
        細(xì)化,下面我們就來看看對(duì)hashCode方法的一致約定要求。



        第一:在某個(gè)運(yùn)行時(shí)期間,只要對(duì)象的(字段的)變化不會(huì)影響equals方法的決策結(jié)果,那么,在這個(gè)期間,無論調(diào)用多少次hashCode,都必須返回同一個(gè)散列碼。
       

        第二:通過equals調(diào)用返回true 的2個(gè)對(duì)象的hashCode一定一樣。

       
        第三:通過equasl返回false 的2個(gè)對(duì)象的散列碼不需要不同,也就是他們的hashCode方法的返回值允許出現(xiàn)相同的情況。
       
        總結(jié)一句話:等價(jià)的(調(diào)用equals返回true)對(duì)象必須產(chǎn)生相同的散列碼。不等價(jià)的對(duì)象,不要求產(chǎn)生的散列碼不相同。
       
       


       


       


       


      hashCode編寫指導(dǎo)


       


      在編寫hashCode時(shí),你需要考慮的是,最終的hash是個(gè)int值,而不能溢出。不同的對(duì)象的hash碼應(yīng)該盡量不同,避免hash沖突。


      那么如果做到呢?下面是解決方案。


       


      1、定義一個(gè)int類型的變量 hash,初始化為 7。


      接下來讓你認(rèn)為重要的字段(equals中衡量相等的字段)參入散列運(yùn),算每一個(gè)重要字段都會(huì)產(chǎn)生一個(gè)hash分量,為最終的hash值做出貢獻(xiàn)(影響)


       
















































































































      運(yùn)算方法參考表
      重要字段var的類型他生成的hash分量
      byte, char, short , int(int)var
      long (int)(var ^ (var >>> 32))
      booleanvar?1:0
      float Float.floatToIntBits(var)
       double long bits = Double.doubleToLongBits(var);
      分量 = (int)(bits ^ (bits >>> 32));
       引用類型  (null == var ? 0 : var.hashCode())

       


       


       


      最后把所有的分量都總和起來,注意并不是簡單的相加。選擇一個(gè)倍乘的數(shù)字31,參與計(jì)算。然后不斷地遞歸計(jì)算,直到所有的字段都參與了。


      復(fù)制代碼

      int hash = 7;
      
      hash = 31 * hash + 字段1貢獻(xiàn)分量;
      
      hash = 31 * hash + 字段2貢獻(xiàn)分量;
      
      .....
      
      return hash;

      復(fù)制代碼

       


       


       


       


      說明,以下的內(nèi)容是我在google上找到并翻譯整理的,其中加入了自己的話和一些例子,便于理解,但我能保證這并不影響整體準(zhǔn)確性。


      英文原文:http://www./journal/2002/10/equalhash.html


       

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多