原文出處:http://blog.chenlb.com/2009/09/hashcode-effect.html
Java 對象 Hashcode 的作用是什嗎?可以聯想資料結構的雜湊表(散列表)、雜湊函數。Object.hashCode() 就是一個雜湊函數,用來計算散列值以實現雜湊表這種資料結構。
看下雜湊表結構:
雜湊表
在一個數組中儲存物件時,通過 hashCode 得到的雜湊值來計算數組的索引位置(通常是求餘運算),然後根據這個索引位置進行存取。多個對象計算出來的索引位置相同(叫hash衝突)時,用鏈表儲存。衝突怎麼保證取到的就是自己呢?那麼就要用到 Object.equals() 方法。
所以要把Object Storage Service在像 hash table類似的資料結構(比如:HashSet)中,hashCode 與 equals 要成對實現。
Java Object hashCode api
返回該對象的雜湊碼值。支援該方法是為雜湊表提供一些優點,例如,java.util.Hashtable 提供的雜湊表。
hashCode 的常規協定是:
- 在 Java 應用程式執行期間,在同一對象上多次調用 hashCode 方法時,必須一致地返回相同的整數,前提是對象上 equals 比較中所用的資訊沒有被修改。從某一應用程式的一次執行到同一應用程式的另一次執行,該整數無需保持一致。
- 如果根據 equals(Object) 方法,兩個對象是相等的,那麼在兩個對象中的每個對象上調用 hashCode 方法都必鬚生成相同的整數結果。
- 以下情況不 是必需的:如果根據 equals(java.lang.Object) 方法,兩個對象不相等,那麼在兩個對象中的任一對象上調用 hashCode 方法必定會產生不同的整數結果。但是,程式員應該知道,為不相等的對象產生不同整數結果可以提高雜湊表的效能。
實際上,由 Object 類定義的 hashCode 方法確實會針對不同的對象返回不同的整數。(這一般是通過將該對象的內部地址轉換成一個整數來實現的,但是 JavaTM 程式設計語言不需要這種實現技巧。)
上面的協定來看,一個對象的狀態(這些狀態不一定是所有欄位,根據業務來看)沒有改變時,多次調用 hashCode 必定相等。但是不同的對象可以有相等的 hashCode,不過盡量使不同的對象有不相等的 hashCode 可以提高雜湊表的效能。
看下 Java Hashtable 的 get 與 put 實現:
- public synchronized V get(Object key) {
- Entry tab[] = table;
- int hash = key.hashCode();
- int index = (hash & 0x7FFFFFFF) % tab.length;
- for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
- if ((e.hash == hash) && e.key.equals(key)) {
- return e.value;
- }
- }
- return null;
- }
先根據 key.hashCode 找數組的索引位置 index,hash & 0x7FFFFFFF 保證是正數。然後順著找 hash 和 equals 相等的。衝突鏈越長效能就越差。
看 put 方法:
- public synchronized V put(K key, V value) {
- // Make sure the value is not null
- if (value == null) {
- throw new NullPointerException();
- }
-
- // Makes sure the key is not already in the hashtable.
- Entry tab[] = table;
- int hash = key.hashCode();
- int index = (hash & 0x7FFFFFFF) % tab.length;
- for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
- if ((e.hash == hash) && e.key.equals(key)) {
- V old = e.value;
- e.value = value;
- return old;
- }
- }
-
- modCount++;
- if (count >= threshold) {
- // Rehash the table if the threshold is exceeded
- rehash();
-
- tab = table;
- index = (hash & 0x7FFFFFFF) % tab.length;
- }
-
- // Creates the new entry.
- Entry<K,V> e = tab[index];
- tab[index] = new Entry<K,V>(hash, key, value, e);
- count++;
- return null;
- }
先看是否有相同的,有就替換。然後數組空間不夠,會重換分配空間並資料重新散列儲存。最後在衝突鏈頭上插入。
其它有用串連:通過分析
JDK 原始碼研究 Hash 儲存機制