簡單的能夠從如下兩個緯度去理解HashMap的底層實現原理。java
HashMap用一個指針數組table,離散化key的做用,當加入一個 key 的時候,經過Hash算法,計算出 key所在的數組下標 i,若是table[i]位置的對象元素爲null的時候,則直接將<key, value>
加入便可;可是,若是table[i]位置已經被佔用的話,則會發生衝突碰撞;此時,會在 table[i]上造成一個鏈表。算法
若是table過小,就會發生頻繁碰撞;此時,查詢時間複雜度由O(1)變爲O(n).
所以,Hash 表的尺寸和容量很是重要。每次當有新的數據要插入Hash 表時,都會檢查容量有沒有超過 thredshold,若是超過,須要擴容 Hash 表,這須要改變從新計算hash分桶的位置—— rehash,這種操做是比較耗時的。數組
因此,在建立HashMap實例的時候須要預先估計一下須要處理的數據量的大小,提早將table的大小和裝載因子load factor設置好,減小Hash碰撞的機率,同時也能夠減小擴容hash表的次數,達到節約時間的目的。函數
當每次添加新元素都是在鏈表頭部添加元素,那麼,問題來了——爲何會形成死鎖呢?按理說每次在鏈表頭部添加元素的話,不可能出現死鎖現象的。spa
問題就出在rehash過程,當將舊table元素轉移到新的newTable的時候,咱們一塊來看看transfer()函數的源碼,分析一下緣由。指針
transfer()源碼以下:code
/** * Transfers all entries from current table to newTable. */ void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; //Step1 : 首先便利索引數組中的元素,Entry<K,V> e 存儲了鏈表的入口元素 for (Entry<K,V> e : table) { //Step2: 對鏈表上的每個元素進行遍歷,從Hash表的頭部第一個元素開始 while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } } }
總結:對象
能夠看到轉移過程是逆序的,轉移前鏈表順序是1->2->3,逆序轉移後新的t順序變成 3->2->1。如今就應該才得八九不離十了,是否是有可能在轉移的過程當中 出現1>2>3>1這種狀況,造成一個頭尾相連的鏈表。索引