HashMap

原則上,hashmap的插入和搜索,複雜度都是1,是很是快速的跟你的容量大小一般是沒有直接關係的可是這是理想的狀況。 這裏說的理想,是在你所存儲的對象的hashcode這個方法寫的很是有效的狀況下。根據hash的原理,存放一個對象是根據他的hashcode來計算的,若是沒有哈希衝突,那麼他的存儲效率是最高,最完美的。 爲何哈希衝突會使得效率降低呢? 具體來分析,假設一個對象O1,他的hashcode算出來是1,另外一個對象是O2,hashcode算起來也是1. 先放入O1對象,這時候速度很快,根據hashcode計算出來這個對象應該在哪一個位置存放,而後直接放進去。可是到了放O2的時候,根據hashcode計算的地址存放,發現以前已經有O1了,那麼顯然是不能放的,所以就要採起些措施,好比,再計算一次,而後分配存放的地址(若是衝突,將繼續,知道解決),一種最惡劣的狀況下,不少不少的對象都存在hash衝突,那麼重要就變得存儲愈來愈慢。可是這個不是hashmap的責任,而是你的對象的hashcode方法沒有定義好,使得衝突頻繁 另外,哈希表爲了不這種衝突,會有一點優化。簡單的說,本來能夠放100個數據的空間,當放到80個的時候,根據經驗,接下去衝突的可能性會更加高,就比如一個靶子上80%都是箭的時候你再射一箭出去,射中箭的可能性很大。所以就自動增長空間來減少衝突可能性。 80/100 = 0.8  這個0.8就是負載因子。 java中的hashmap的負載因子是0.75說了寫理論。說這個的緣由是想解釋一下你的疑問「10000條的時候在搜索的時候很快,那麼在多少條的時候可能致使效率降低呢」。這個答案是確定的,就是存儲的量跟存儲效率沒有直接的關係。 這頁是hash表這個數據結構的優點所在 若是你以爲效率出現問題的時候,應該去關注一下你的存儲對象的hashcode方法寫的是否有問題 若是想更完美的解決效率問題,還能夠手動指定hashmap的負載因子(用HashMap(int factor)這個構造方法),負載因子越低,衝突可能越小。可是犧牲的空間會相應增長html

若是仍是不能很好理解,能夠先參看hash這個數據結構的特色,和JDK中HashMap的源代碼,以及註釋java

學好java,數據結構是很重要的,理解原理的使用,跟生搬硬套的使用,不可同年而語 因此,去面試淘寶,騰訊,化爲這種公司不會問你struts怎麼用,只會問你struts怎麼寫。如同不會問你hashmap怎麼用,而會問你hashmap的設計理念,和實現原理面試

1.HashMap的數據結構

  數組的特色是:尋址容易,插入和刪除困難;而鏈表的特色是:尋址困難,插入和刪除容易。那麼咱們能不能綜合二者的特性,作出一種尋址容易,插入刪除也容易的數據結構?答案是確定的,這就是咱們要提起的哈希表,哈希表有多種不一樣的實現方法,我接下來解釋的是最經常使用的一種方法—— 拉鍊法,咱們能夠理解爲「鏈表的數組」 ,如圖:算法

  從上圖咱們能夠發現哈希表是由數組+鏈表組成的,一個長度爲16的數組中,每一個元素存儲的是一個鏈表的頭結點。那麼這些元素是按照什麼樣的規則 存儲到數組中呢。通常狀況是經過hash(key)%len得到,也就是元素的key的哈希值對數組長度取模獲得。好比上述哈希表 中,12%16=12,28%16=12,108%16=12,140%16=12。因此十二、2八、108以及140都存儲在數組下標爲12的位置。數組

  HashMap其實也是一個線性的數組實現的,因此能夠理解爲其存儲數據的容器就是一個線性數組。這可能讓咱們很不解,一個線性的數組怎麼實現按鍵值對來存取數據呢?這裏HashMap有作一些處理。數據結構

  1.首先HashMap裏面實現一個靜態內部類Entry,其重要的屬性有 key , value, next,從屬性key,value咱們就能很明顯的看出來Entry就是HashMap鍵值對實現的一個基礎bean,咱們上面說到HashMap的基 礎就是一個線性數組,這個數組就是Entry[],Map裏面的內容都保存在Entry[]裏面。性能

2.HashMap的存取實現

     既然是線性數組,爲何能隨機存取?這裏HashMap用了一個小算法,大體是這樣實現:優化

複製代碼
//存儲時: int hash = key.hashCode();// 這個hashCode方法這裏不詳述,只要理解每一個key的hash是一個固定的int值 int index = hash % Entry[].length; Entry[index] = value; //取值時: int hash = key.hashCode(); int index = hash % Entry[].length; return Entry[index];
複製代碼

到這裏咱們輕鬆的理解了HashMap經過鍵值對實現存取的基本原理this

    3.疑問:若是兩個key經過hash%Entry[].length獲得的index相同,會不會有覆蓋的危險?spa

  這裏HashMap裏面用到鏈式數據結構的一個概念。上面咱們提到過Entry類裏面有一個next屬性,做用是指向下一個Entry。打個比 方, 第一個鍵值對A進來,經過計算其key的hash獲得的index=0,記作:Entry[0] = A。一會後又進來一個鍵值對B,經過計算其index也等於0,如今怎麼辦?HashMap會這樣作:B.next = A,Entry[0] = B,若是又進來C,index也等於0,那麼C.next = B,Entry[0] = C;這樣咱們發現index=0的地方其實存取了A,B,C三個鍵值對,他們經過next這個屬性連接在一塊兒。因此疑問不用擔憂。也就是說數組中存儲的是最後插入的元素。到這裏爲止,HashMap的大體實現,咱們應該已經清楚了。

  固然HashMap裏面也包含一些優化方面的實現,這裏也說一下。好比:Entry[]的長度必定後,隨着map裏面數據的愈來愈長,這樣同一 個index的鏈就會很長,會不會影響性能?HashMap裏面設置一個因素(也稱爲因子),隨着map的size愈來愈大,Entry[]會以必定的規 則加長長度。

3.解決hash衝突的辦法

  1. 開放定址法(線性探測再散列,二次探測再散列,僞隨機探測再散列)
  2. 再哈希法
  3. 鏈地址法
  4. 創建一個公共溢出區

Java中hashmap的解決辦法就是採用的鏈地址法。

 

參考文獻:

一、http://www.cnblogs.com/xwdreamer/archive/2012/05/14/2499339.html

相關文章
相關標籤/搜索