public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key); 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; }
首先對key作null檢查。若是key是null,會被存儲到table[0],由於null的hash值老是0。java
key的hashcode()方法會被調用,而後計算hash值。hash值用來找到存儲Entry對象的數組的索引。有時候hash函數可能寫的很很差,因此JDK的設計者添加了另外一個叫作hash()的方法,它接收剛纔計算的hash值做爲參數。若是你想了解更多關於hash()函數的東西,能夠參考:hashmap中的hash和indexFor方法算法
indexFor(hash,table.length)用來計算在table數組中存儲Entry對象的精確的索引。數組
在咱們的例子中已經看到,若是兩個key有相同的hash值(也叫衝突),他們會以鏈表的形式來存儲。因此,這裏咱們就迭代鏈表。函數
· 若是在剛纔計算出來的索引位置沒有元素,直接把Entry對象放在那個索引上。性能
· 若是索引上有元素,而後會進行迭代,一直到Entry->next是null。當前的Entry對象變成鏈表的下一個節點。this
· 若是咱們再次放入一樣的key會怎樣呢?邏輯上,它應該替換老的value。事實上,它確實是這麼作的。在迭代的過程當中,會調用equals()方法來檢查key的相等性(key.equals(k)),若是這個方法返回true,它就會用當前Entry的value來替換以前的value。spa
public V get(Object key) { if (key == null) return getForNullKey(); Entry<K,V> entry = getEntry(key); return null == entry ? null : entry.getValue(); }
當你傳遞一個key從hashmap總獲取value的時候:設計
對key進行null檢查。若是key是null,table[0]這個位置的元素將被返回。code
key的hashcode()方法被調用,而後計算hash值。對象
indexFor(hash,table.length)用來計算要獲取的Entry對象在table數組中的精確的位置,使用剛纔計算的hash值。
在獲取了table數組的索引以後,會迭代鏈表,調用equals()方法檢查key的相等性,若是equals()方法返回true,get方法返回Entry對象的value,不然,返回null。
簡單地說,HashMap 在底層將 key-value 當成一個總體進行處理,這個總體就是一個 Entry 對象。HashMap 底層採用一個 Entry[] 數組來保存全部的 key-value 對,當須要存儲一個 Entry 對象時,會根據hash算法來決定其在數組中的存儲位置,在根據equals方法決定其在該數組位置上的鏈表中的存儲位置;當須要取出一個Entry時, 也會根據hash算法找到其在數組中的存儲位置,再根據equals方法從該位置上的鏈表中取出該Entry。
當hashmap中的元素愈來愈多的時候,碰撞的概率也就愈來愈高(由於數組的長度是固定的),因此爲了提升查詢的效率,就要對hashmap的數組進行擴容,數組擴容這個操做也會出如今ArrayList中,因此這是一個通用的操做,不少人對它的性能表示過懷疑,不過想一想咱們的「均攤」原理,就釋然了,而在hashmap數組擴容以後,最消耗性能的點就出現了:原數組中的數據必須從新計算其在新數組中的位置,並放進去,這就是resize。 那麼hashmap何時進行擴容呢?當hashmap中的元素個數超過數組大小*loadFactor時,就會進行數組擴容,loadFactor的默認值爲0.75,也就是說,默認狀況下,數組大小爲16,那麼當hashmap中元素個數超過16*0.75=12的時候,就把數組的大小擴展爲2*16=32,即擴大一倍,而後從新計算每一個元素在數組中的位置,而這是一個很是消耗性能的操做,因此若是咱們已經預知hashmap中元素的個數,那麼預設元素的個數可以有效的提升hashmap的性能。好比說,咱們有1000個元素new HashMap(1000), 可是理論上來說new HashMap(1024)更合適,不過上面annegu已經說過,即便是1000,hashmap也自動會將其設置爲1024。 可是new HashMap(1024)還不是更合適的,由於0.75*1000 < 1000, 也就是說爲了讓0.75 * size > 1000, 咱們必須這樣new HashMap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。