Java.lang.Object 有一個hashCode()和一個equals()方法,這兩個方法在軟件設計中扮演着舉足輕重的角色。在一些類中覆寫這兩個方法以完成某些重要功能。本文描述了爲何要用hashCode(), 如何使用,以及其餘的一些擴展。閱讀本文須要有基本的hash算法知識以及基本的Java集合知識,本文屬於菜鳥入門級講解,大神讀至此請點擊右上角的X,以避免浪費您的時間^_^。java
WHY hashCode()?算法
集合Set中的元素是無序不可重複的,那判斷兩個元素是否重複的依據是什麼呢? 「比較對象是否相等固然用Object.equal()了」,某猿如是說。可是,Set中存在大量對象,後添加到集合Set中的對象元素比較次數會逐漸增多,大大下降了程序運行效率。 Java中採用哈希算法(也叫散列算法)來解決這個問題,將對象(或數據)依特定算法直接映射到一個地址上,對象的存取效率大大提升。這樣一來,當含有海量元素的集合Set須要添加某元素(對象)時,先調用這個元素的hashCode(),就能一會兒定位到此元素實際存儲位置,若是這個位置沒有元素,說明此對象時第一次存儲到集合Set, 直接將此對象存儲在此位置上;若此位置有對象存在,調用equal()看看這兩個對象是否相等,相等就捨棄此元素不存,不等則散列到其餘地址。 HOW use hashCode()?jvm
Java語言對猿設計equal()有五個必須遵循的要求。ide
對稱性。若 a.equal(b) 返回」true」, 則 b.equal(a) 也必須返回 「true」. 反射性。a.equal(a) 必須返回」true」. 傳遞性。若a.equal(b) 返回 「true」, 且 b.equal(c)返回 「true」, 則c.equal(a)必返回」true」. 一致性。若a.equal(b) 返回」true」, 只要a, b內容不變,無論重複多少次a.equal(b)必須返回」true」. 任何狀況下,a.equals(null),永遠返回是「false」;a.equals(和a不一樣類型的對象)永遠返回是「false」. hashCode()的返回值和equals()的關係.函數
若是a.equals(b)返回「true」,那麼a和b的hashCode()必須相等。 若是a.equals(b)返回「false」,那麼a和b的hashCode()有可能相等,也有可能不等。 下面是一個例子。在實際的軟件開發中,最好重寫這兩個方法。性能
public class Employee { int employeeId; String name; // other methods would be in here @Override public boolean equals(Object obj) { if(obj==this) return true; Employee emp=(Employee)obj; if(employeeId.equals(emp.getEmployeeId()) && name==emp.getName()) return true; return false; } @Override public int hashCode() { int hash = 1; hash = hash * 17 + employeeId; hash = hash * 31 + name.hashCode(); return hash; } }
下面着重介紹一下經常使用類的hashCode()實現方法。優化
String類的hasCode()this
Java代碼設計
public int hashCode() { int h = hash; if (h == 0) { int off = offset; char val[] = value; int len = count; for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; } return h; }
這段代碼最有意思的仍是hash的實現方法了。最終計算的hash值爲: s[0]31n-1 + s[1]31n-2 + … + s[n-1]指針
s[i]是string的第i個字符,n是String的長度。那爲何這裏用31,而不是其它數呢?
31是個奇素數,若是乘數是偶數,而且乘法溢出的話,信息就會丟失,由於與2相乘等價於移位運算。使用素數的好處並非很明顯,可是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,能夠獲得更好的性能:31*i==(i<<5)-i。如今的VM能夠自動完成這種優化。(From Effective Java)
Object類的hasCode()
Object類中hashCode()是一個Native方法。Native方法如何調用?
public native int hashCode();
Object類的Native方法類可在這裏找到。 深刻分析請看另一篇博客
static JNINativeMethod methods[] = { {"hashCode", "()I", (void *)&JVM_IHashCode}, {"wait", "(J)V", (void *)&JVM_MonitorWait}, {"notify", "()V", (void *)&JVM_MonitorNotify}, {"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll}, {"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, };
源代碼包括getClass()(See line58)等, hashCode()(See line43)被定義爲一個指向JVM_IHashCode指針。
jvm.cpp中定義了JVM_IHashCode(line 504)函數, 此函數裏調用ObjectSynchronizer::FastHashCode,其定在 synchronizer.cpp, 可參考576行的FastHashCode 和 530行的 get_next_hash 的實現。