Effective Java Item 9 note
Always override hashCode when you override equals
如果你有override了equals的話,那麼一定也要override hashCode,如果沒有override hashCode,除了違反Object.hashCode的規定(第二條)之外(因為你沒有保證兩個equals的物件產生一樣的hashCode,sample code放在github上面 https://bit.ly/2IQEfOr),也讓基於hash-base的collection沒辦法使用(HashMap, HashSet, HashTable)
Java的Object規定:
- 在application runtime, 只要object的equals所用到的值沒有被修改,那麽多次對同一個object使用,其hashCode必須每次都是同一個整數。
- 如果兩個對象透過equals比較是相等的,那麼這兩個對象的hashCode也會是一樣的整數結果
- 如果兩個對象透過equals比較是不相等的,那麼這兩個對象的hashCode不一定要是不同的結果,但如果產生不同結果的話是會提高hash table的效能的
那麼hashCode內容要怎麼寫呢?
可以使用IDE應該會有generator幫產生equals跟hashCode。那大原則是盡可能分散各object到hash table中,一個極端的例子是都產生同一個hashCode的話,那麼會讓hash table退化成linked-list,效能就從O(n) -> O(n²)
底下是各種primitive type用android studio auto generate的範例:
@Override
public int hashCode() {
int result;
long temp;
result = a; //result值可任選,有些是17,基本上就是避免碰撞
// 31是odd prime,然後可以用shift跟減法來代替乘法,31 * i == ( i << 5) - I;
result = 31 * result + (int) (b ^ (b >>> 32));
result = 31 * result + (int) c;
result = 31 * result + (d != +0.0f ? Float.floatToIntBits(d) : 0);
result = 31 * result + (e ? 1 : 0);
temp = Double.doubleToLongBits(f);
result = 31 * result + (int) (temp ^ (temp >>> 32));
result = 31 * result + t.hashCode();
return result;
}
切記equals跟hashCode基本上是綁一起的,hashCode當中的計算只會用到有在equals裡面使用的變數,如果使用到equals裡面用到的變數之外的值也是有可能違反上面提到的第二個條件的。
然後書上也有提到,如果一個class是immutable,而且計算hashCode的負擔比較大的話,那們就可以考慮把hashCode存在object裡面,不用每次都算,那如果這個object會被當成是hash key的話,那就可以在constructor的時候就算,不然可以用lazily initialize,等到hashCode被用到的時候再算。
最後不要以為可以提高效能然後企圖移掉任何一個object裡面被拿來算hashCode的值,等到存有大量object的時候就會知道因小失大了。