Java hashCode() 方法深入理解
Java.lang.Object 有一個hashCode()和一個equals()方法,這兩個方法在軟體設計中扮演著舉足輕重的角色。在一些類中覆寫這兩個方法以完成某些重要功能。本文描述了為什麼要用hashCode(), 如何使用,以及其他的一些擴充套件。閱讀本文需要有基本的hash演算法知識以及基本的Java集合知識,本文屬於菜鳥入門級講解,大神讀至此請點選右上角的X,以免浪費您的時間^_^。
WHY hashCode()?
集合Set中的元素是無序不可重複的,那判斷兩個元素是否重複的依據是什麼呢? “比較物件是否相等當然用Object.equal()了”,某猿如是說。但是,Set中存在大量物件,後新增到集合Set中的物件元素比較次數會逐漸增多,大大降低了程式執行效率。 Java中採用雜湊演算法(也叫雜湊演算法)來解決這個問題,將物件(或資料)依特定演算法直接對映到一個地址上,物件的存取效率大大提高。這樣一來,當含有海量元素的集合Set需要新增某元素(物件)時,先呼叫這個元素的hashCode(),就能一下子定位到此元素實際儲存位置,如果這個位置沒有元素,說明此物件時第一次儲存到集合Set, 直接將此物件儲存在此位置上;若此位置有物件存在,呼叫equal()看看這兩個物件是否相等,相等就捨棄此元素不存,不等則雜湊到其他地址。
HOW use hashCode()?
Java語言對猿設計equal()有五個必須遵循的要求。
- 對稱性。若 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()
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 的實現。
相關文章
- 深入理解 Java 方法Java
- 搞懂 Java equals 和 hashCode 方法Java
- java之深入理解List.subList()方法Java
- Java基礎系列-equals方法和hashCode方法Java
- 『Java 語法基礎』對 equals() 和 hashCode() 的理解Java
- 深入理解Java PriorityQueueJava
- 深入理解Java反射Java反射
- Java:IO:深入理解Java
- 深入理解Java HelloWorldJava
- Java原始碼之String的HashCode方法Java原始碼
- Java hashCode() 指南Java
- 如何正確實現Java中的hashCode方法Java
- 深入理解 Java 註解Java
- 深入理解 Java 泛型Java泛型
- 深入理解 Java 列舉Java
- Java集合——深入理解HashMapJavaHashMap
- 深入理解 Java 陣列Java陣列
- 深入理解Java異常Java
- 深入理解Java反射(一)Java反射
- 深入理解Java泛型Java泛型
- java中的hashCodeJava
- Java Object 之hashCodeJavaObject
- [Java基礎]HashcodeJava
- java為什麼要重寫hashCode和equals方法Java
- 深入理解 Java 序列化Java
- 深入理解Java I/O模型Java模型
- 深入理解Java中的鎖Java
- 深入理解 Java 中的 LambdaJava
- 深入理解Java中的AQSJavaAQS
- Java基礎——深入理解反射Java反射
- Java 集合深入理解(3):CollectionJava
- 深入理解Java的==和equalsJava
- 深入探討、理解Java的CLASSPATHJava
- 深入理解Java物件結構Java物件
- Java重寫equals方法時為什麼要重寫hashcode方法Java
- 細說equals()方法和hashCode()方法
- 詳解equals()方法和hashCode()方法
- 重新實現hashCode()方法