【老實李】HashMap的底層原理探索

通過幾個問題來學習HashMap

前提大家都知道,HashMap是由哈希表實現的,哈希表就是由數組和鏈表組成的。

給出一個很形象的數據結構圖。

image.png

問題1.既然HashMap是數組+鏈表實現的,數組開始的時候一定是有一個固定長度的,那HashMap中的數組默認長度是多少呢?

默認情況下,內部數組的長度就是16,這個可以從HashMap的底層源碼的構造函數中看到。下面我們就開始HashMap的源碼閱讀之旅。

HashMap的構造函數有三個。默認我們都是不會傳這個initialCapacity的,如果不傳的話,那么就會使用默認為4的DEFAULT_INITIAL_CAPACITY


DEFAULT_INITIAL_CAPACITY
HashMap的構造函數

然后將initialCapacity的值賦給了threshold,我們會在put方法中判斷,如果是第一次put數據就會初始化Table,也就使用到了這個threshold。

put

那么是怎么初始化的呢?就是拿到這個threshold對其做一下平方。(roundUpToPowerOf2就是對2的冪次冪)

image.png
roundUpToPowerOf2就是對2的冪次冪

一步步追蹤源代碼,發現最后是返回的默認的4的平方的數組長度。

問題2.HashMap既然底層是一個線性的數組,那么是怎么實現的隨機存取呢?

(因為是隨機存取,所以是有些索引位置是沒有元素的,會產生一些空間的浪費,但是這其實就是空間換時間,讓HashMap既有數組的查詢快,又有鏈表的增刪快的優點)

// 存儲時:
int hash = key.hashCode(); 
int index = hash % Entry[].length;
Entry[index] = value;

// 取值時:
int hash = key.hashCode();
int index = hash % Entry[].length;
return Entry[index];

存儲的時候就是拿到key的hash值然后對HashMap底層數組的長度取余,取余的結果就是存儲的索引。
取值的時候一樣是拿到key的hash值然后對HashMap底層數組的長度取余,得到索引直接去數組里面取就行了。取出來是一個鏈表的封裝類Entry,然后遍歷一下Entry中的值取出我們要的就行了。


image.png
image.png

總結:元素存儲的規則 hash(key)%len, 就是key的hash值對HashMap底層數組的長度取余,這個公式一定要記住。

問題3.通過上面的問題我們了解了HashMap的存取,但是我們要知道Hash算法其實就是把任意長度的輸入變換成固定長度的輸出,這種轉換是一種壓縮映射,也就是說,Hash值的空間遠小于輸入的空間,不同的輸入可能會Hash成相同的輸出。而且我們又對HashMap默認size 16的數組長度取余,所以不同的key就更是有很大概率返回相同的索引了,那會不會就把之前存的數據給覆蓋了呢?

答案當然是否定的。不然誰還敢用HashMap存數據呢。

HashMap我們知道是數組+鏈表實現的,前面我們是只看到了數組,鏈表呢就是在這使用的。這里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這個屬性鏈接在一起。也就是說數組中存儲的是最后插入的元素。(數組中只會存一個Entry元素,這一個元素的next就會指向下一個元素,這樣循環)

問題4.我們前面看到數組的默認長度是16,可以說很小,那他會不會擴容呢?

答案是肯定的。默認HashMap內部數組的長度為16,負載因子為0.75,就是在構造函數里面傳的兩個值。閾值就是12(16*0.75=12),這樣當第十三個元素加入時,底層數組就會擴容。擴容為原數組大小的兩倍。

image.png

resize(2*table.length)就是擴容的操作。擴容為原數組的兩倍。

擴容為原數組的兩倍
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1.HashMap是一個數組+鏈表/紅黑樹的結構,數組的下標在HashMap中稱為Bucket值,每個數組項對應的...
    誰在烽煙彼岸閱讀 1,040評論 2 2
  • 實際上,HashSet 和 HashMap 之間有很多相似之處,對于 HashSet 而言,系統采用 Hash 算...
    曹振華閱讀 2,524評論 1 37
  • 今天感覺突然想不出主題了,上課時倒是聽到老師說的很多有觸動的話,今天就先說這句吧。 “如果文章寫不下去了,怎么辦?...
    小小恙閱讀 445評論 0 0