HashMap的工做原理

2019-05-07 22:30:55ide

1、概述函數

從本文你能夠學習到:性能

  • 何時會使用HashMap?他有什麼特色?
  • 你知道HashMap的工做原理嗎?
  • 你知道get和put的原理嗎?equals()和hashCode()的都有什麼做用?
  • 你知道hash的實現嗎?爲何要這樣實現?
  • 若是HashMap的大小超過了負載因子(load factor)定義的容量,怎麼辦?

在官方文檔中是這樣描述HashMap的:學習

Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.ui

幾個關鍵的信息:基於Map接口實現、容許null鍵/值、非同步、不保證有序(好比插入的順序)、也不保證序不隨時間變化。spa

 

2、兩個重要的參數設計

在HashMap中有兩個很重要的參數,容量(Capacity)和負載因子(Load factor)。code

  • Initial capacity :The capacity is the number of buckets in the hash table, The initial capacity is simply the capacity at the time the hash table is created.
  • Load factor :The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased.

簡單的說,Capacity就是buckets的數目,Load factor就是buckets填滿程度的最大比例。若是對迭代性能要求很高的話不要把capacity設置過大,也不要把load factor設置太小。當bucket填充的數目(即hashmap中元素的個數)大於capacity*load factor時就須要調整buckets的數目爲當前的2倍。對象

【注】load factor默認值爲0.75blog

 

3、put函數的實現

put函數大體的思路爲:

  1. 對key的hashCode()作hash,而後再計算index;
  2. 若是沒碰撞直接放到bucket裏;
  3. 若是碰撞了,以鏈表的形式存在buckets後;
  4. 若是碰撞致使鏈表過長(大於等於TREEIFY_THRESHOLD),就把鏈表轉換成紅黑樹;
  5. 若是節點已經存在就替換old value(保證key的惟一性)
  6. 若是bucket滿了(超過load factor*current capacity),就要resize。

 

4、get函數的實現

在理解了put以後,get就很簡單了。大體思路以下:

  • bucket裏的第一個節點,直接命中;
  • 若是有衝突,則經過key.equals(k)去查找對應的entry。
  • 若爲樹,則在樹中經過key.equals(k)查找,O(logn);若爲鏈表,則在鏈表中經過key.equals(k)查找,O(n)。

 

5、hash函數的實現

在get和put的過程當中,計算下標時,先對hashCode進行hash操做,而後再經過hash值進一步計算下標,以下圖所示:

在設計hash函數時,由於目前的table長度n爲2的冪,而計算下標的時候,使用(n - 1) & hash來限制獲得的idx的大小在capacity內。

 

6、Resize的實現

當put時,若是發現目前的bucket佔用程度已經超過了Load Factor所但願的比例,那麼就會發生resize。在resize的過程,簡單的說就是把bucket擴充爲2倍,以後從新計算index,把節點再放到新的bucket中。resize的註釋是這樣描述的:

Initializes or doubles table size. If null, allocates in accord with initial capacity target held in field threshold. Otherwise, because we are using power-of-two expansion, the elements from each bin must either stay at same index, or move with a power of two offset in the new table.

 大體意思就是說,當超過限制的時候會resize,然而又由於咱們使用的是2次冪的擴展(指長度擴爲原來2倍),因此,元素的位置要麼是在原位置,要麼是在原位置再移動2次冪的位置。

 

7、總結

咱們如今能夠回答開始的幾個問題,加深對HashMap的理解:

1. 何時會使用HashMap?他有什麼特色?
是基於Map接口的實現,存儲鍵值對時,它能夠接收null的鍵值,是非同步的,HashMap存儲着Entry(hash, key, value, next)對象。

2. 你知道HashMap的工做原理嗎?
經過hash的方法,經過put和get存儲和獲取對象。存儲對象時,咱們將K/V傳給put方法時,它調用hashCode計算hash從而獲得bucket位置,進一步存儲,HashMap會根據當前bucket的佔用狀況自動調整容量(超過Load Facotr則resize爲原來的2倍)。獲取對象時,咱們將K傳給get,它調用hashCode計算hash從而獲得bucket位置,並進一步調用equals()方法肯定鍵值對。若是發生碰撞的時候,Hashmap經過鏈表將產生碰撞衝突的元素組織起來,在Java 8中,若是一個bucket中碰撞衝突的元素超過某個限制(默認是8),則使用紅黑樹來替換鏈表,從而提升速度。

3. 你知道get和put的原理嗎?equals()和hashCode()的都有什麼做用?
經過對key的hashCode()進行hashing,並計算下標( n-1 & hash),從而得到buckets的位置。若是產生碰撞,則利用key.equals()方法去鏈表或樹中去查找對應的節點

4. 你知道hash的實現嗎?爲何要這樣實現?
在Java 1.8的實現中,是經過hashCode()的高16位異或低16位實現的:(h = k.hashCode()) ^ (h >>> 16),主要是從速度、功效、質量來考慮的,這麼作能夠在bucket的n比較小的時候,也能保證考慮到高低bit都參與到hash的計算中,同時不會有太大的開銷。

5. 若是HashMap的大小超過了負載因子(load factor)定義的容量,怎麼辦?若是超過了負載因子(默認0.75),則會從新resize一個原來長度兩倍的HashMap,而且從新調用hash方法。

相關文章
相關標籤/搜索