重寫equals()必須重寫hashCode()

如果不被重寫(原生Object)的hashCode和equals是什麼樣的?

  1.   不被重寫(原生)的hashCode值是根據內存地址換算出來的一個值。
  2.   不被重寫(原生)的equals方法是嚴格判斷一個對象是否相等的方法(object1 == object2)。

 

爲什麼需要重寫equals和hashCode方法?
      在我們的業務系統中判斷對象時有時候需要的不是一種嚴格意義上的相等,而是一種業務上的對象相等。在這種情況下,原生的equals方法就不能滿足我們的需求了
      所以這個時候我們需要重寫equals方法,來滿足我們的業務系統上的需求。那麼爲什麼在重寫equals方法的時候需要重寫hashCode方法呢?

      我們先來看一下Object.hashCode的通用約定:

  • 在一個應用程序執行期間,如果一個對象的equals方法做比較所用到的信息沒有被修改的話,那麼,對該對象調用hashCode方法多次,它必須始終如一地返回 同一個整數。在同一個應用程序的多次執行過程中,這個整數可以不同,即這個應用程序這次執行返回的整數與下一次執行返回的整數可以不一致。
  • 如果兩個對象根據equals(Object)方法是相等的,那麼調用這兩個對象中任一個對象的hashCode方法必須產生同樣的整數結果。
  • 如果兩個對象根據equals(Object)方法是不相等的,那麼調用這兩個對象中任一個對象的hashCode方法,不要求必須產生不同的整數結果。然而,程序員應該意識到這樣的事實,對於不相等的對象產生截然不同的整數結果,有可能提高散列表(hash table)的性能。
  • 如果只重寫了equals方法而沒有重寫hashCode方法的話,則會違反約定的第二條:相等的對象必須具有相等的散列碼(hashCode)。

         同時對於HashSet和HashMap這些基於散列值(hash)實現的類。HashMap的底層處理機制是以數組的方法保存放入的數據的(Node<K,V>[] table),其中的關鍵是數組下標的處理。數組的下標是根據傳入的元素hashCode方法的返回值再和特定的值異或決定的。如果該數組位置上已經有放入的值了,且傳入的鍵值相等則不處理,若不相等則覆蓋原來的值,如果數組位置沒有條目,則插入,並加入到相應的鏈表中。檢查鍵是否存在也是根據hashCode值來確定的。所以如果不重寫hashCode的話,可能導致HashSet、HashMap不能正常的運作、

        如果我們將某個自定義對象存到HashMap或者HashSet及其類似實現類中的時候,如果該對象的屬性參與了hashCode的計算,那麼就不能修改該對象參數hashCode計算的屬性了。有可能會移除不了元素,導致內存泄漏。

下面看一下java.lang.String類中hashCode()和equals()方法的實現重寫:

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }
  public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

 在HashMap中的put(key,value),containsKey(key),get(key)都有類似如下的判斷:

final Entry<K,V> getEntry(Object key) {
        if (size == 0) {
            return null;
        }

        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

必須是hash值相同&&(地址相同或者equals相同(以String爲例,equals比較的實際上是值是否相同)才爲真,如果只重寫了equals,而兩個String對象如果都是通過new String()創建的,肯定地址不相同,Object中默認的hashCode()方法是計算地址返回一個int值,肯定不相同。會導致hashMap,hashSet等不能正常使用。

注意: equals相同,hashcode一定相同。equals不同,hashCode()

在Integer包裝類中,hashCode()方法更是直接重寫爲了return value, 所以只要兩個Integer對象的值相同,兩個對象的hashcode都相同。

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