重寫hashCode和equals方法
當在雜湊集合中放入key時,將自動檢視key物件的hashCode值,若此時放置的hashCode值和原來已有的hashCode值相等,則自動呼叫equals()方法,若此時返回的為true則表示該key為相同的key值,只會存在一份。
Object中關於hashCode和equals方法的定義為:
Java程式碼 public boolean equals(Object obj) {
return (this == obj);
}
public native int hashCode();
基類的hashCode是一個native方法,訪問作業系統底層,它得到的值是與這個物件在記憶體中的地址有關。
Object的不同子類對於equals和hashCode方法有其自身的實現方式,如Integer和String等。
equals相等的,hashCode必須相等
hashCode不等的,則 equals也必定不等。
hashCode相等的 equals不一定相等(但最好少出現 hashCode相等的情況)。
HashMap的put(K, Value)方法提供了一個根據K的hashCode來計算Hash碼的方法hash()
Java程式碼transient Entry[] table; public V put(K key, V value) { if (key == null) return putForNullKey(value); //HashMap支援null的key int hash = hash(key.hashCode()); //根據key的hashCode計算Hash值 int i = indexFor(hash, table.length); //搜尋指定Hash值在對應table中的索引 for (Entry<K,V> e = table[i]; e != null; e = e.next) { //在i索引處Entry不為空,迴圈遍歷e的下一個元素 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; } } //若i索引處Entry為null,表明此處還沒有Entry modCount++; addEntry(hash, key, value, i); //將key、value新增到i索引處 return null; } static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
對於任意給定的物件,只有它的hashCode()返回值相同,那麼程式呼叫hash(int h)方法所計算得到的Hash碼值總是相同的。接下來會呼叫indexFor(int h, int length)方法來計算該物件應該儲存在table陣列的哪個索引處。
Java程式碼 static int indexFor(int h, int length) {
return h & (length-1);
}
它總是通過h & (table.length - 1)來得到該物件的儲存位置--而HashMap底層陣列的長度總是2的n次方,這樣就保證了得到的索引值總是位於table陣列的索引之內。
當通過key-value放入HashMap時,程式就根據key的hashCode()來覺得Entry的儲存位置:若兩個Entry的key的hashCode()相同那麼他們的儲存位置相同;若兩個Entry的key的equals()方法返回true則新新增Entry的value將覆蓋原有Entry的value,但key不會覆蓋;若兩個Entry的key的equals()方法返回false則新加的Entry與集合中原有的Entry形成Entry鏈。
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold) //size儲存了HashMap中所包含的key-value對的數量
resize(2 * table.length); //擴充table物件的長度2倍
}
HashSet的add(E)的實現是通過HashMap的put方法來實現的。(HashSet內部是通過HashMap來實現的,TreeSet則是通過TreeMap來實現的)
Java程式碼 public V get(Object key) {
if (key == null)
return getForNullKey();
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)))
return e.value;
}
return null;
}
根據key的hashCode計算器Hash值,然後取得該Hash值在表table的索引,取得該索引i對應的table中的Entry,判斷key的equals()。