經常會有人問,如果重寫了equals方法,是不是一定要重寫hashcode方法呢? 答案是yes,因為如果不重寫,那麼基於hash結構的那些東東比如hashMap,HashSet等等會出現非你想象的結果。
why?看一下HashMap的源碼,get也是一樣的判斷邏輯:
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
可以看出會先判斷hash,而hash是經過hashCode計算出來的,所以如果hashCode不相同,就會被當做兩個不同的索引值對,所以取出來的就未必是你想要的結果了。
從上面代碼也可以看出,如果只重寫hashCode而不重寫equals也是不行的,
if (e.hash == hash && ((k = e.key) == key || key.equals(k))),==比較的是記憶體位址與hashCode無關(這個結論寫個簡單的例子就可以驗證),每一個new都是新的對象,互不==的,所以大部分情況還是會走到equals,所以equals也同等重要,要重寫就兩者都重寫哦~其實,java對於hashCode是有約定的:
1. 在一個應用程式執行期間,如果一個對象的equals方法做比較所用到的資訊沒有被修改的話,則對該對象調用hashCode方法多次,它必須始終如一地返回同一個整數。在多次執行期間,當然可以不同。
2. 如果兩個對象根據equals(Object o)方法是相等的,則調用這兩個對象中任一對象的hashCode方法必須產生相同的整數結果。
3. 如果兩個對象根據equals(Object o)方法是不相等的,則調用這兩個對象中任一個對象的hashCode方法
如何理解第三條最後一句?散列的意義在於其速度,散列的速度是優於線性結構的,所以如果散列碼衝突的很多,後面跟著一個長長的鏈表,那散列還談何速度呢?