重寫hashCode和equals方法

如果你的對象想散列存儲的集合中或者想作爲散列Map的Key時(HashSet、HashMap、Hashtable等)那麼你必須重寫equals()方法,這樣才能保證唯一性。在重寫equals()方法的同時,必須重寫hashCode()方法?當然,在這種情況下,你不想重寫hashCode()方法,也沒有錯,但是sun建議這麼做,重寫hashCode只是技術要求(爲了提高效率)。

      當在散列集合中放入key時,將自動查看key對象的hashCode值,若此時放置的hashCode值和原來已有的hashCode值相等,則自動調用equals()方法,若此時返回的爲true則表示該key爲相同的key值,只會存在一份。

      Object中關於hashCode和equals方法的定義爲:

Java代碼  收藏代碼

[java] view plaincopy
  1. public boolean equals(Object obj) {    
  2.     return (this == obj);    
  3. }    
  4. public native int hashCode();    


      基類的hashCode是一個native方法,訪問操作系統底層,它得到的值是與這個對象在內存中的地址有關。

      Object的不同子類對於equals和hashCode方法有其自身的實現方式,如Integer和String等。
            equals相等的,hashCode必須相等 
            hashCode不等的,則 equals也必定不等。 
            hashCode相等的 equals不一定相等(但最好少出現 hashCode相等的情況)。

      HashMap的put(K, Value)方法提供了一個根據K的hashCode來計算Hash碼的方法hash() 

Java代碼  收藏代碼

[java] view plaincopy
  1. transient Entry[] table;    
  2. public V put(K key, V value) {    
  3.     if (key == null)    
  4.         return putForNullKey(value);        //HashMap支持null的key    
  5.     int hash = hash(key.hashCode());        //根據key的hashCode計算Hash值    
  6.     int i = indexFor(hash, table.length);   //搜索指定Hash值在對應table中的索引    
  7.     for (Entry<K,V> e = table[i]; e != null; e = e.next) {    //在i索引處Entry不爲空,循環遍歷e的下一個元素    
  8.         Object k;    
  9.         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {    
  10.             V oldValue = e.value;    
  11.             e.value = value;    
  12.             e.recordAccess(this);    
  13.             return oldValue;    
  14.         }    
  15.     }    
  16.     //若i索引處Entry爲null,表明此處還沒有Entry    
  17.     modCount++;    
  18.     addEntry(hash, key, value, i);  //將key、value添加到i索引處    
  19.     return null;    
  20. }    
  21.     
  22. static int hash(int h) {    
  23.     h ^= (h >>> 20) ^ (h >>> 12);    
  24.     return h ^ (h >>> 7) ^ (h >>> 4);    
  25. }    


     對於任意給定的對象,只有它的hashCode()返回值相同,那麼程序調用hash(int h)方法所計算得到的Hash碼值總是相同的。接下來會調用indexFor(int h, int length)方法來計算該對象應該保存在table數組的哪個索引處。 

Java代碼  收藏代碼
[java] view plaincopy
  1. static int indexFor(int h, int length) {    
  2.     return h & (length-1);    
  3. }    


     它總是通過h & (table.length - 1)來得到該對象的保存位置--而HashMap底層數組的長度總是2的n次方,這樣就保證了得到的索引值總是位於table數組的索引之內。
     當通過key-value放入HashMap時,程序就根據key的hashCode()來覺得Entry的存儲位置:若兩個Entry的key的hashCode()相同那麼他們的存儲位置相同;若兩個Entry的key的equals()方法返回true則新添加Entry的value將覆蓋原有Entry的value,但key不會覆蓋;若兩個Entry的key的equals()方法返回false則新加的Entry與集合中原有的Entry形成Entry鏈。

Java代碼  收藏代碼
[java] view plaincopy
  1. void addEntry(int hash, K key, V value, int bucketIndex) {    
  2.     Entry<K,V> e = table[bucketIndex];    
  3.     table[bucketIndex] = new Entry<K,V>(hash, key, value, e);    
  4.     if (size++ >= threshold)     //size保存了HashMap中所包含的key-value對的數量    
  5.         resize(2 * table.length);   //擴充table對象的長度2倍    
  6. }    


     HashSet的add(E)的實現是通過HashMap的put方法來實現的。(HashSet內部是通過HashMap來實現的,TreeSet則是通過TreeMap來實現的)

Java代碼  收藏代碼
[java] view plaincopy
  1. public V get(Object key) {    
  2.     if (key == null)    
  3.         return getForNullKey();    
  4.     int hash = hash(key.hashCode());    
  5.     int i = indexFor(hash, table.length);    
  6.     for (Entry<K,V> e = table[i]; e != null; e = e.next) {    
  7.         Object k;    
  8.         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))    
  9.             return e.value;    
  10.     }    
  11.     return null;    
  12. }    


    根據key的hashCode計算器Hash值,然後取得該Hash值在表table的索引,取得該索引i對應的table中的Entry,判斷key的equals()。

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