Hash又叫散列,輸入任意長度的位元組,通過雜湊演算法,轉換成固定長度的雜湊值;
雜湊列表的實現是通過對key進行雜湊,得到的雜湊值來做為儲存資料的數組的索引,這樣會有一個問題是不同的key的雜湊值有可能會重複,所以我們在數組儲存位上建立一個鏈表來儲存相同雜湊值的資料
在java中就是通過引用,數組,鏈表來實現的hashmap
我們先看hashmap中存放資料的結構:
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; final int hash; Entry<K,V> next; ........... }
每個Entry會有一個指向下一個資料的引用,這就在同一個槽位上構成了鏈表,最先加入的放在鏈表頭部,後加入的放在尾部。
很顯然只有當我們能夠均勻的將hash值分布在數組的所有槽位上的時候,這個時候我們對鏈表操作是最少的,效率是最高的。
基本的hash定址演算法有除法散列法,平方散列法,斐波那契(Fibonacci)散列法等,但是java是這樣做的:
static int indexFor(int h, int length) { return h & (length-1); }
java會用key的hashcode值與數組的槽數-1進行與運算
這裡會有一個問題只有當數組的槽數為2的n次方-1,其二進位全是1的(如2的2次方-1=11)的時候雜湊值產生碰撞的機率是最小的
所以在java中hashmap的數組的初始大小是16(2的4次方)
hashmap的resize:
當不斷put資料使資料慢慢變大的時候,剛開始的數組已經不能滿足需求了,我們需要擴大數組的槽數
hashmap中有loadFactor屬性,該屬性預設為0.75,即元素個數達到數組的百分之七十五的時候,數組槽數會進行翻倍,並且之前已存入的資料會重新進行計算。
so:如果我們可以預估我們會在hashmap中存放1000個資料,那麼我們就要確保數組的槽數乘上0.75大於1000,我們得到1366,如果我們這樣寫new HashMap(1366),java會自動幫我們轉換成new HashMap(2048)(2的n次方)