在重寫了對象的equals方法後,還需要重寫hashCode方法嗎?

首先說建議的情況:  比如你的對象想放到Set集合或者是想作爲Map的key時(非散列的Set和Map,例如TreeSet,TreeMap等),那麼你必須重寫equals()方法,這樣才能保證唯一性。當然,在這種情況下,你不想重寫hashCode()方法,也沒有錯。但是,對於良好的編程風格而言,你應該在重寫equals()方法的同時,也重寫hashCode()方法。 

然後再說說必須重寫hashCode()的情況: 
    如果你的對象想放進散列存儲的集合中(比如:HashSet,LinkedHashSet)或者想作爲散列Map(例如:HashMap,LinkedHashMap等等)的Key時,在重寫equals()方法的同時,必須重寫hashCode()方法。 

這裏我想講講sun的設計者爲什麼需要設計hashcode方法,也許這樣你就應該知道什麼時候該重寫它了。 
數據結構有一種爲了提高查找的效率而存在的數據結構——散列表,散列表其實是普通數組概念的推廣,因爲可以對數組進行直接尋址,故可以再O(1)時間內訪問數組的任意元素,thinking in java中有個對hashmap簡單的實現,我們來看看你就明白了: 

 

  1. //: containers/SimpleHashMap.java  
  2. // A demonstration hashed Map.  
  3. import java.util.*;  
  4. import net.mindview.util.*;  
  5.   
  6. public class SimpleHashMap<K,V> extends AbstractMap<K,V> {  
  7.   // Choose a prime number for the hash table  
  8.   // size, to achieve a uniform distribution:  
  9.   static final int SIZE = 997;  
  10.   // You can't have a physical array of generics,  
  11.   // but you can upcast to one:  
  12.   @SuppressWarnings("unchecked")  
  13.   LinkedList<MapEntry<K,V>>[] buckets =  
  14.     new LinkedList[SIZE];//List數組裏每項是個List,數組下標是hashcode方法的返回值再經過散列函數得到的,相當於散列表的關鍵字,它亦代表着每個對象的關鍵字。(不能顯示new一個泛型數組,但是你可以new一個泛型數組的引用,如有需要以後可以將普通數組轉型爲泛型數組)。 
  15.   public V put(K key, V value) {//將這個對鍵值放進hashmap中。
  16.     V oldValue = null;  
  17.     int index = Math.abs(key.hashCode()) % SIZE;//這裏就是通過散列函數對hashcode的返回值處理得到一個關鍵字,它代表了對象在數組裏的位置,既是數組下標。
  18.     if(buckets[index] == null)  
  19.       buckets[index] = new LinkedList<MapEntry<K,V>>();//如果是第一次散列到這個數組下標,那麼就新生成一個LinkedList,可以看到它裏面保存的是MapEntry<K,V>鍵和值。 
  20.     LinkedList<MapEntry<K,V>> bucket = buckets[index];//將這個LinkedList賦值給一個bucket(桶),以後就直接在這個bucket進行操作。
  21.     MapEntry<K,V> pair = new MapEntry<K,V>(key, value);  
  22.     boolean found = false;  
  23.     ListIterator<MapEntry<K,V>> it = bucket.listIterator();  
  24.     while(it.hasNext()) {  
  25.       MapEntry<K,V> iPair = it.next();  
  26.       if(iPair.getKey().equals(key)) {//如果已經存在同一個鍵值,那麼就更新value。 
  27.         oldValue = iPair.getValue();  
  28.         it.set(pair); // Replace old with new  
  29.         found = true;  
  30.         break;  
  31.       }  
  32.     }  
  33.     if(!found)  
  34.       buckets[index].add(pair);//如果是一個新的鍵值,那麼直接添加到這個LinkedList中。
  35.     return oldValue;  
  36.   }  
  37.   public V get(Object key) {//看hashmap是如何憑藉hashcode方法快速定位到鍵值。 
  38.     int index = Math.abs(key.hashCode()) % SIZE;[color=red]//與put方法中的作用一樣,生成數組下標,因爲我存的時候就是存到這個地方的,那麼我取的時候直接訪問buckets[index]。[/color]  
  39.     if(buckets[index] == nullreturn null;//直接訪問這個數組下標的LinkedList,如果爲null,則返回。
  40.     for(MapEntry<K,V> iPair : buckets[index])//爲什麼要用LinkedList,因爲hashcode方法產生的散列碼不能完全確定一個對象,也就是說會和其他對象發生“碰撞”,即散列到同一個數組下標,解決這個問題的組號辦法就是定義一個List把它們保存起來,但是在這個List中,我們必須保證能用equals方法確定對象的身份,這也就是爲什麼很多人說hashcode()相等,equals()不一定相等,而equals()相等的兩個對象,hashcode()一定相等。所以這裏要直接在LinkedList執行線性查找。
  41.       if(iPair.getKey().equals(key))  
  42.         return iPair.getValue();  
  43.     return null;  
  44.   }  
  45.   public Set<Map.Entry<K,V>> entrySet() {  
  46.     Set<Map.Entry<K,V>> set= new HashSet<Map.Entry<K,V>>();  
  47.     for(LinkedList<MapEntry<K,V>> bucket : buckets) {  
  48.       if(bucket == nullcontinue;  
  49.       for(MapEntry<K,V> mpair : bucket)  
  50.         set.add(mpair);  
  51.     }  
  52.     return set;  
  53.   }  
  54.   public static void main(String[] args) {  
  55.     SimpleHashMap<String,String> m =  
  56.       new SimpleHashMap<String,String>();  
  57.     m.putAll(Countries.capitals(25));  
  58.     System.out.println(m);  
  59.     System.out.println(m.get("ERITREA"));  
  60.     System.out.println(m.entrySet());  
  61.   }  
  62. /* Output: 
  63. {CAMEROON=Yaounde, CONGO=Brazzaville, CHAD=N'djamena, COTE D'IVOIR (IVORY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Libreville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, GHANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa} 
  64. Asmara 
  65. [CAMEROON=Yaounde, CONGO=Brazzaville, CHAD=N'djamena, COTE D'IVOIR (IVORY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Libreville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, GHANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa] 


怎樣?現在應該知道hashcode方法的作用了吧,它就是用來提高效率的,有句話說得好:爲速度而散列。因爲散列的Set和Map是基於hashcode方法來查找對象的,所以你在使用這些類的時候一定要覆蓋hashcode方法,而非散列的Set和Map,例如TreeSet,TreeMap等,它們只需equals方法就可以唯一確定對象的身份。這樣說,想必已經很清楚了吧。

發佈了6 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章