記錄一下雜湊表底層原理
理解HashMap底層,首先應該理解Hash函式
從解決一個問題入手:大量的資料要儲存查詢,構造雜湊表來解決
初步想法
借鑑陣列下標訪問的思路來做,只需知道起始位置和下標值,
不管陣列中有多少個元素,都可以一次訪問到,
將元素和元素位置建立一種一一對應的關係
Hash函式的出現
輸入的元素的範圍可能很大甚至無窮,而我們的記憶體有限,
所以說我們需要一種函式對映關係,將這些無限的元素對映到我們有限的記憶體地址上。
Hash函式代表著一類函式,即把任意範圍的元素可以通過對映關係壓縮成固定範圍的元素。
Hash函式的選擇
如果是正整數,我們可以用這個正整數數除以某個數,取其餘數,即我們常用的 k % m,k為正整數,m 為除數;
這樣一來,範圍就縮小了很多,比如說 15%10=5,26%10=6,...,所有的正整數經過運算,都變成了 0-9 範圍之間的數了,
這樣範圍就縮小了很多
m 的選擇
這種做法,m 的選擇就非常重要了,如果 k 值分佈均勻還無所謂,如果 k 值具有某些特徵
比如說 k 的個位基本上不變,而高位分佈均勻,如 15,25,45,65,85,95,155,就遭遇大沖突了,
必須要使得經過Hash函式後關鍵字的分佈均勻,儘量減少衝突
鏈地址法
為了解決衝突,引出鏈地址法
在儲存的時候,如果多個元素被Hash到同一位置,那麼就加入到該位置所指向的連結串列中,
如果該位置沒有元素,則為null(指向空)”
由於新加入的元素很可能被再次訪問到,使用“頭插”
rehash
這樣解決衝突固然好,但是也有瓶頸
當我們實際存入的值越來越多的時候,這個連結串列也勢必越來越長,
那當我們進行查詢的時候,勢必就會遍歷連結串列,效率也就越來越慢。
因此,我們要選取一個相關的新的Hash函式(比如之前使用 key % m,現在只改變一下m的值)
將舊Hash表中所有的元素通過新的Hash函式計算出新的Hash值,並將其插入到新表中(仍然使用連結串列),這就叫rehash
這裡的陣列就擴大了近兩倍,由於要大小要選素數,那就選原陣列大小兩倍後的第一個素數7,舊Hash表和新Hash表採用了不同的Hash函式,但相關,只是m的取值變了
裝載因子 α
我們可以定義這樣一個變數 α = 所有元素個數/陣列的大小,
它代表著我們的Hash表(也就是陣列)的裝滿程度,在這裡也代表連結串列的平均長度
這個裝載因子代表了Hash表的裝滿程度,這裡也可以代表連結串列的平均長度,那麼也就可以代表查詢時的時間長短了。