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

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

    • 分享

      轉(zhuǎn) Java中equals()和hasCode()的方法詳解_NightMare

       仙人不留果 2009-12-04

      1. 首先equals()和hashcode()這兩個方法都是從object類中繼承過來的。
      equals()方法在object類中定義如下:
      public boolean equals(Object obj) {
      return (this == obj);
      }
      很明顯是對兩個對象的地址值進(jìn)行的比較(即比較引用是否相同)。但是我們必需清楚,當(dāng)String 、Math、還有Integer、Double。。。。等這些封裝類在使用equals()方法時,已經(jīng)覆蓋了object類的equals()方法。比如在String類中如下:
      public boolean equals(Object anObject) {
      if (this == anObject) {
          return true;
      }
      if (anObject instanceof String) {
          String anotherString = (String)anObject;
          int n = count;
          if (n == anotherString.count) {
      char v1[] = value;
      char v2[] = anotherString.value;
      int i = offset;
      int j = anotherString.offset;
      while (n-- != 0) {
          if (v1[i++] != v2[j++])
      return false;
      }
      return true;
          }
      }
      return false;
      }
      很明顯,這是進(jìn)行的內(nèi)容比較,而已經(jīng)不再是地址的比較。依次類推Double、Integer、Math。。。。等等這些類都是重寫了equals()方法的,從而進(jìn)行的是內(nèi)容的比較。當(dāng)然了基本類型是進(jìn)行值的比較,這個沒有什么好說的。
      我們還應(yīng)該注意,Java語言對equals()的要求如下,這些要求是必須遵循的:
      • 對稱性:如果x.equals(y)返回是“true”,那么y.equals(x)也應(yīng)該返回是“true”。
      • 反射性:x.equals(x)必須返回是“true”。
      • 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也應(yīng)該返回是“true”。
      • 還有一致性:如果x.equals(y)返回是“true”,只要x和y內(nèi)容一直不變,不管你重復(fù)x.equals(y)多少次,返回都是“true”。
      • 任何情況下,x.equals(null),永遠(yuǎn)返回是“false”;x.equals(和x不同類型的對象)永遠(yuǎn)返回是“false”。
      以上這五點(diǎn)是重寫equals()方法時,必須遵守的準(zhǔn)則,如果違反會出現(xiàn)意想不到的結(jié)果,請大家一定要遵守。
      2. 其次是hashcode() 方法,在object類中定義如下:
      public native int hashCode();
      說明是一個本地方法,它的實(shí)現(xiàn)是根據(jù)本地機(jī)器相關(guān)的。當(dāng)然我們可以在自己寫的類中覆蓋hashcode()方法,比如String、Integer、Double。。。。等等這些類都是覆蓋了hashcode()方法的。例如在String類中定義的hashcode()方法如下:
          public int hashCode() {
      int h = hash;
      if (h == 0) {
          int off = offset;
          char val[] = value;
          int len = count;

                  for (int i = 0; i < len; i++) {
                      h = 31*h + val[off++];
                  }
                  hash = h;
              }
              return h;
      }
      解釋一下這個程序(String的API中寫到):
      s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
      使用 int 算法,這里 s[i] 是字符串的第 i 個字符,n 是字符串的長度,^ 表示求冪。(空字符串的哈希碼為 0。)

      首先,想要明白hashCode的作用,你必須要先知道Java中的集合?! ?br>總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。
      你知道它們的區(qū)別嗎?前者集合內(nèi)的元素是有序的,元素可以重復(fù);后者元素?zé)o序,但元素不可重復(fù)。
      那么這里就有一個比較嚴(yán)重的問題了:要想保證元素不重復(fù),可兩個元素是否重復(fù)應(yīng)該依據(jù)什么來判斷呢?
      這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那么當(dāng)元素很多時,后添加到集合中的元素比較的次數(shù)就非常多了。
      也就是說,如果集合中現(xiàn)在已經(jīng)有1000個元素,那么第1001個元素加入集合時,它就要調(diào)用1000次equals方法。這顯然會大大降低效率。   
      于是,Java采用了哈希表的原理。哈希(Hash)實(shí)際上是個人名,由于他提出一哈希算法的概念,所以就以他的名字命名了。
      哈希算法也稱為散列算法,是將數(shù)據(jù)依特定算法直接指定到一個地址上。如果詳細(xì)講解哈希算法,那需要更多的文章篇幅,我在這里就不介紹了。
      初學(xué)者可以這樣理解,hashCode方法實(shí)際上返回的就是對象存儲的物理地址(實(shí)際可能并不是)。  
      這樣一來,當(dāng)集合要添加新的元素時,先調(diào)用這個元素的hashCode方法,就一下子能定位到它應(yīng)該放置的物理位置上。
      如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進(jìn)行任何比較了;如果這個位置上已經(jīng)有元素了,
      就調(diào)用它的equals方法與新元素進(jìn)行比較,相同的話就不存了,不相同就散列其它的地址。
      所以這里存在一個沖突解決的問題。這樣一來實(shí)際調(diào)用equals方法的次數(shù)就大大降低了,幾乎只需要一兩次。  
      所以,Java對于eqauls方法和hashCode方法是這樣規(guī)定的:
      1、如果兩個對象相同,那么它們的hashCode值一定要相同;2、如果兩個對象的hashCode相同,它們并不一定相同     上面說的對象相同指的是用eqauls方法比較。  
      你當(dāng)然可以不按要求去做了,但你會發(fā)現(xiàn),相同的對象可以出現(xiàn)在Set集合中。同時,增加新元素的效率會大大下降。


      3.這里我們首先要明白一個問題:
      equals()相等的兩個對象,hashcode()一定相等;
      equals()不相等的兩個對象,卻并不能證明他們的hashcode()不相等。換句話說,equals()方法不相等的兩個對象,hashcode()有可能相等。(我的理解是由于哈希碼在生成的時候產(chǎn)生沖突造成的)。
      反過來:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解釋下第3點(diǎn)的使用范圍,我的理解是在object、String等類中都能使用。在object類中,hashcode()方法是本地方法,返回的是對象的地址值,而object類中的equals()方法比較的也是兩個對象的地址值,如果equals()相等,說明兩個對象地址值也相等,當(dāng)然hashcode()也就相等了;在String類中,equals()返回的是兩個對象內(nèi)容的比較,當(dāng)兩個對象內(nèi)容相等時,
      Hashcode()方法根據(jù)String類的重寫(第2點(diǎn)里面已經(jīng)分析了)代碼的分析,也可知道hashcode()返回結(jié)果也會相等。以此類推,可以知道Integer、Double等封裝類中經(jīng)過重寫的equals()和hashcode()方法也同樣適合于這個原則。當(dāng)然沒有經(jīng)過重寫的類,在繼承了object類的equals()和hashcode()方法后,也會遵守這個原則。

      4.談到hashcode()和equals()就不能不說到hashset,hashmap,hashtable中的使用,具體是怎樣呢,請看如下分析:
      Hashset是繼承Set接口,Set接口又實(shí)現(xiàn)Collection接口,這是層次關(guān)系。那么hashset是根據(jù)什么原理來存取對象的呢?
      在hashset中不允許出現(xiàn)重復(fù)對象,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重復(fù)的呢?這就是問題的關(guān)鍵所在,經(jīng)過一下午的查詢求證終于獲得了一點(diǎn)啟示,和大家分享一下,在java的集合中,判斷兩個對象是否相等的規(guī)則是:
      1),判斷兩個對象的hashCode是否相等
            如果不相等,認(rèn)為兩個對象也不相等,完畢
            如果相等,轉(zhuǎn)入2)
      (這一點(diǎn)只是為了提高存儲效率而要求的,其實(shí)理論上沒有也可以,但如果沒有,實(shí)際使用時效率會大大降低,所以我們這里將其做為必需的。后面會重點(diǎn)講到這個問題。)
      2),判斷兩個對象用equals運(yùn)算是否相等
            如果不相等,認(rèn)為兩個對象也不相等
            如果相等,認(rèn)為兩個對象相等(equals()是判斷兩個對象是否相等的關(guān)鍵)
      為什么是兩條準(zhǔn)則,難道用第一條不行嗎?不行,因為前面已經(jīng)說了,hashcode()相等時,equals()方法也可能不等,所以必須用第2條準(zhǔn)則進(jìn)行限制,才能保證加入的為非重復(fù)元素。
      比如下面的代碼:

      public static void main(String args[]){
      String s1=new String("zhaoxudong");
      String s2=new String("zhaoxudong");
      System.out.println(s1==s2);//false
      System.out.println(s1.equals(s2));//true
      System.out.println(s1.hashCode());//s1.hashcode()等于s2.hashcode()
      System.out.println(s2.hashCode());
      Set hashset=new HashSet();
      hashset.add(s1);
      hashset.add(s2);
      /*實(shí)質(zhì)上在添加s1,s2時,運(yùn)用上面說到的兩點(diǎn)準(zhǔn)則,可以知道hashset認(rèn)為s1和s2是相等的,是在添加重復(fù)元素,所以讓s2覆蓋了s1;*/
      Iterator it=hashset.iterator();
                  while(it.hasNext())
                  {
                   System.out.println(it.next());
                  }
      最后在while循環(huán)的時候只打印出了一個”zhaoxudong”。
      輸出結(jié)果為:false
                  true
                  -967303459
                  -967303459
      這是因為String類已經(jīng)重寫了equals()方法和hashcode()方法,所以在根據(jù)上面的第1.2條原則判定時,hashset認(rèn)為它們是相等的對象,進(jìn)行了重復(fù)添加。
      但是看下面的程序:
      import java.util.*;
      public class HashSetTest
      {
         public static void main(String[] args)
          {
                       HashSet hs=new HashSet();
                       hs.add(new Student(1,"zhangsan"));
                       hs.add(new Student(2,"lisi"));
                       hs.add(new Student(3,"wangwu"));
                       hs.add(new Student(1,"zhangsan"));

                       Iterator it=hs.iterator();
                       while(it.hasNext())
                       {
                              System.out.println(it.next());
                       }
           }
      }
      class Student
         {
           int num;
           String name;
           Student(int num,String name)
                      {
                      this.num=num;
                       this.name=name;
                       }
                    public String toString()
                      {
                          return num+":"+name;
                       }
                 }     
      輸出結(jié)果為:
                            1:zhangsan
                         1:zhangsan
                         3:wangwu
                         2:lisi
      問題出現(xiàn)了,為什么hashset添加了相等的元素呢,這是不是和hashset的原則違背了呢?回答是:沒有
      因為在根據(jù)hashcode()對兩次建立的new Student(1,"zhangsan")對象進(jìn)行比較時,生成的是不同的哈希碼值,所以hashset把他當(dāng)作不同的對象對待了,當(dāng)然此時的equals()方法返回的值也不等(這個不用解釋了吧)。那么為什么會生成不同的哈希碼值呢?上面我們在比較s1和s2的時候不是生成了同樣的哈希碼嗎?原因就在于我們自己寫的Student類并沒有重新自己的hashcode()和equals()方法,所以在比較時,是繼承的object類中的hashcode()方法,呵呵,各位還記得object類中的hashcode()方法比較的是什么吧!!
      它是一個本地方法,比較的是對象的地址(引用地址),使用new方法創(chuàng)建對象,兩次生成的當(dāng)然是不同的對象了(這個大家都能理解吧。。。),造成的結(jié)果就是兩個對象的hashcode()返回的值不一樣。所以根據(jù)第一個準(zhǔn)則,hashset會把它們當(dāng)作不同的對象對待,自然也用不著第二個準(zhǔn)則進(jìn)行判定了。那么怎么解決這個問題呢??
      答案是:在Student類中重新hashcode()和equals()方法。
      例如:
      class Student
      {
      int num;
      String name;
      Student(int num,String name)
      {
                  this.num=num;
                  this.name=name;
      }
      public int hashCode()
      {
                  return num*name.hashCode();
      }
      public boolean equals(Object o)
      {
                  Student s=(Student)o;
                  return num==s.num && name.equals(s.name);
      }
      public String toString()
      {
                  return num+":"+name;
      }
      }
      根據(jù)重寫的方法,即便兩次調(diào)用了new Student(1,"zhangsan"),我們在獲得對象的哈希碼時,根據(jù)重寫的方法hashcode(),獲得的哈希碼肯定是一樣的(這一點(diǎn)應(yīng)該沒有疑問吧)。
      當(dāng)然根據(jù)equals()方法我們也可判斷是相同的。所以在向hashset集合中添加時把它們當(dāng)作重復(fù)元素看待了。所以運(yùn)行修改后的程序時,我們會發(fā)現(xiàn)運(yùn)行結(jié)果是:
                            1:zhangsan
                         3:wangwu
                         2:lisi
      可以看到重復(fù)元素的問題已經(jīng)消除。
      關(guān)于在hibernate的pojo類中,重新equals()和hashcode()的問題:
      1),重點(diǎn)是equals,重寫hashCode只是技術(shù)要求(為了提高效率)
      2),為什么要重寫equals呢,因為在java的集合框架中,是通過equals來判斷兩個對象是否相等的
      3),在hibernate中,經(jīng)常使用set集合來保存相關(guān)對象,而set集合是不允許重復(fù)的。我們再來談?wù)勄懊嫣岬皆谙騢ashset集合中添加元素時,怎樣判斷對象是否相同的準(zhǔn)則,前面說了兩條,其實(shí)只要重寫equals()這一條也可以。
      但當(dāng)hashset中元素比較多時,或者是重寫的equals()方法比較復(fù)雜時,我們只用equals()方法進(jìn)行比較判斷,效率也會非常低,所以引入了hashcode()這個方法,只是為了提高效率,但是我覺得這是非常有必要的(所以我們在前面以兩條準(zhǔn)則來進(jìn)行hashset的元素是否重復(fù)的判斷)。
      比如可以這樣寫:
      public int hashCode(){
         return 1;}//等價于hashcode無效
      這樣做的效果就是在比較哈希碼的時候不能進(jìn)行判斷,因為每個對象返回的哈希碼都是1,每次都必須要經(jīng)過比較equals()方法后才能進(jìn)行判斷是否重復(fù),這當(dāng)然會引起效率的大大降低。
      我有一個問題,如果像前面提到的在hashset中判斷元素是否重復(fù)的必要方法是equals()方法(根據(jù)網(wǎng)上找到的觀點(diǎn)),但是這里并沒有涉及到關(guān)于哈希表的問題,可是這個集合卻叫hashset,這是為什么??
      我想,在hashmap,hashtable中的存儲操作,依然遵守上面的準(zhǔn)則。所以這里不再多說。這些是今天看書,網(wǎng)上查詢資料,自己總結(jié)出來的,部分代碼和語言是引述,但是千真萬確是自己總結(jié)出來的。有錯誤之處和不詳細(xì)不清楚的地方還請大家指出,我也是初學(xué)者,所以難免會有錯誤的地方,希望大家共同討論

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

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多