1. 程式人生 > >hashMap源碼中的一個細節問題

hashMap源碼中的一個細節問題

存儲位置 條件 shm 不同 () his span nbsp pre

public V put(K key, V value) { if (key == null) return putForNullKey(value); 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))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }

以上代碼是hashMap中存儲的實現,簡單講下它的原理。hashMap實現自Map接口,他是一個雙列集合,以鍵值

對的形式儲存數據,存儲方式利用哈希表散列,數據結構是一個可變數組與鏈表相結合。這個數組中存儲的數

據是一個Entry類型的對象,該類裏面維護了一個Key、Value以及指向下一個對象的next變量,Entry類也就使

其具備了存儲鍵值對以及數組鏈表相結合的條件。

當往map中put數據時,首先判斷key值是否為空,為空則直接將value放入數組的的第一個位置,不為空,則計算

出key的hashcode值,根據hashcode的二次hash結果以及數組的大小計算出該數據所在的存儲位置,該存儲位置

的數據未必為空,因此先判斷計算出的位置上是否有數據,如果沒有,直接將value值存進去,如果有,則開始

遍歷該位置所保存的鏈表,判斷有沒有key值相同的,如果有相同的,則用value覆蓋該位置上的值,如果遍歷完

了也沒有,則將value值插在這個鏈表的頭部,插入就算完成了。

過程很簡單,但有一個細節剛開始很令人費解:

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

就是此處的判斷語句,此時我們已經找到了新存儲數據應該存儲的位置,但是在該位置上已經有數據了,而且這些

數據的key有可能和新數據的key相同,也有可能不同,因為我們雖然是使用key的hash進行的位置計算,但位置相同

的key,其內容未必相同,因此上述代碼就是判斷key值是否相同的,判斷方式就是先比較hash值,如果相同再比較

內容。此時又令人費解的是&& 後面沒有直接跟equals的比較而是((k = e.key) == key || key.equals(k)),

其實他是等價於key.equals(k),上述的寫法其實是一種優化效率的體現,因為我們經常會對某些對象重寫其equals

方法來比較內容,倘若(k = e.key) == key都返回true了,那換有必要再調用equals嗎?這種情況下就會有效率

的提升。

hashMap源碼中的一個細節問題