在學習 HashMap 前,咱們先來溫習下 Hash(哈希) 的概念。html
Hash(哈希),又稱「散列」。java
散列(hash)英文原意是「混雜」、「拼湊」、「從新表述」的意思。web
在某種程度上,散列是與排序相反的一種操做,排序是將集合中的元素按照某種方式好比字典順序排列在一塊兒,而散列經過計算哈希值,打破元素之間原有的關係,使集合中的元素按照散列函數的分類進行排列。算法
在介紹一些集合時,咱們總強調須要重寫某個類的 equlas() 方法和 hashCode() 方法,確保惟一性。這裏的 hashCode() 表示的是對當前對象的惟一標示。計算 hashCode 的過程就稱做 哈希。數組
咱們一般使用數組或者鏈表來存儲元素,一旦存儲的內容數量特別多,須要佔用很大的空間,並且在查找某個元素是否存在的過程當中,數組和鏈表都須要挨個循環比較,而經過 哈希 計算,能夠大大減小比較次數。緩存
如今有 4 個數 {2,5,9,13},須要查找 13 是否存在。服務器
int[] numbers = new int[]{2,5,9,13}; for (int i = 0; i < numbers.length; i++) { if (numbers[i] == 13){ System.out.println("find it!"); return; } }
這樣須要遍歷 4 次才能找到,時間複雜度爲 O(n)。markdown
H[key] = key % 3;
四個數 {2,5,9,13} 對應的哈希值爲:網絡
H[2] = 2 % 3 = 2; H[5] = 5 % 3 = 2; H[9] = 9 % 3 = 0; H[13] = 13 % 3 = 1;
而後把它們存儲到對應的位置。數據結構
當要查找 13 時,只要先使用哈希函數計算它的位置,而後去那個位置查看是否存在就行了,本例中只需查找一次,時間複雜度爲 O(1)。
好比你和我同樣是個剁手族買書狂,家裏書一大堆,若是書存放時不分類直接擺到書架上(數組存儲),找某本書時可能須要腦殼從左往右從上往下轉好幾圈才能發現;若是存放時按照類別分開放,技術書、小說、文學等等分開(按照某種哈希函數計算),找書時只要從它對應的分類裏找,天然省事多了。
哈希的過程當中須要使用哈希函數進行計算。
哈希函數是一種映射關係,根據數據的關鍵詞 key ,經過必定的函數關係,計算出該元素存儲位置的函數。
表示爲:
address = H [key]
構造哈希函數的方法不少,實際工做中要根據不一樣的狀況選擇合適的方法,總的原則是儘量少的產生衝突。
一般考慮的因素有關鍵字的長度和分佈狀況、哈希值的範圍等。
如:當關鍵字是整數類型時就能夠用除留餘數法;若是關鍵字是小數類型,選擇隨機數法會比較好。
選用哈希函數計算哈希值時,可能不一樣的 key 會獲得相同的結果,一個地址怎麼存放多個數據呢?這就是衝突。
經常使用的主要有兩種方法解決衝突:
1.連接法(拉鍊法)
拉鍊法解決衝突的作法是:
將全部關鍵字爲同義詞的結點連接在同一個單鏈表中。
若選定的散列表長度爲 m,則可將散列表定義爲一個由 m 個頭指針組成的指針數組 T[0..m-1] 。
凡是散列地址爲 i 的結點,均插入到以 T[i] 爲頭指針的單鏈表中。
T 中各份量的初值均應爲空指針。
在拉鍊法中,裝填因子 α 能夠大於 1,但通常均取 α ≤ 1。
2.開放定址法
用開放定址法解決衝突的作法是:
用開放定址法解決衝突的作法是:當衝突發生時,使用某種探測技術在散列表中造成一個探測序列。沿此序列逐個單元地查找,直到找到給定的關鍵字,或者碰到一個開放的地址(即該地址單元爲空)爲止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。查找時探測到開放的地址則代表表中無待查的關鍵字,即查找失敗。
簡單的說:當衝突發生時,使用某種探查(亦稱探測)技術在散列表中尋找下一個空的散列地址,只要散列表足夠大,空的散列地址總能找到。
按照造成探查序列的方法不一樣,可將開放定址法區分爲線性探查法、二次探查法、雙重散列法等。
a.線性探查法
hi=(h(key)+i) % m ,0 ≤ i ≤ m-1
基本思想是:
探查時從地址 d 開始,首先探查 T[d],而後依次探查 T[d+1],…,直到 T[m-1],此後又循環到 T[0],T[1],…,直到探查到 有空餘地址 或者到 T[d-1]爲止。
b.二次探查法
hi=(h(key)+i*i) % m,0 ≤ i ≤ m-1
基本思想是:
探查時從地址 d 開始,首先探查 T[d],而後依次探查 T[d+1^2],T[d+2^2],T[d+3^2],…,等,直到探查到 有空餘地址 或者到 T[d-1]爲止。
缺點是沒法探查到整個散列空間。
c.雙重散列法
hi=(h(key)+i*h1(key)) % m,0 ≤ i ≤ m-1
基本思想是:
探查時從地址 d 開始,首先探查 T[d],而後依次探查 T[d+h1(d)], T[d + 2*h1(d)],…,等。
該方法使用了兩個散列函數 h(key) 和 h1(key),故也稱爲雙散列函數探查法。
定義 h1(key) 的方法較多,但不管採用什麼方法定義,都必須使 h1(key) 的值和 m 互素,才能使發生衝突的同義詞地址均勻地分佈在整個表中,不然可能形成同義詞地址的循環計算。
該方法是開放定址法中最好的方法之一。
哈希表(hash table)是哈希函數最主要的應用。
哈希表是實現關聯數組(associative array)的一種數據結構,普遍應用於實現數據的快速查找。
用哈希函數計算關鍵字的哈希值(hash value),經過哈希值這個索引就能夠找到關鍵字的存儲位置,即桶(bucket)。哈希表不一樣於二叉樹、棧、序列的數據結構通常狀況下,在哈希表上的插入、查找、刪除等操做的時間複雜度是 O(1)。
查找過程當中,關鍵字的比較次數,取決於產生衝突的多少,產生的衝突少,查找效率就高,產生的衝突多,查找效率就低。所以,影響產生衝突多少的因素,也就是影響查找效率的因素。
影響產生衝突多少有如下三個因素:
哈希表的加載因子和容量決定了在何時桶數(存儲位置)不夠,須要從新哈希。
加載因子太大的話桶太多,遍歷時效率變低;太大的話頻繁 rehash,致使性能下降。因此加載因子的大小須要結合時間和空間效率考慮。
在 HashMap 中的加載因子爲 0.75,即四分之三。
網絡環境下的分佈式緩存系統通常基於一致性哈希(Consistent hashing)。簡單的說,一致性哈希將哈希值取值空間組織成一個虛擬的環,各個服務器與數據關鍵字K使用相同的哈希函數映射到這個環上,數據會存儲在它順時針「遊走」遇到的第一個服務器。可使每一個服務器節點的負載相對均衡,很大程度上避免資源的浪費。
在動態分佈式緩存系統中,哈希算法的設計是關鍵點。使用分佈更合理的算法可使得多個服務節點間的負載相對均衡,能夠很大程度上避免資源的浪費以及部分服務器過載。 使用帶虛擬節點的一致性哈希算法,能夠有效地下降服務硬件環境變化帶來的數據遷移代價和風險,從而使分佈式緩存系統更加高效穩定。
http://www.nowamagic.net/librarys/veda/detail/1273
http://blog.csdn.net/cywosp/article/details/23397179/
http://www.cnblogs.com/qiaoshanzi/p/5295554.html
http://baike.baidu.com/view/549615.htm
http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/chazhao/chazhao9.4.3.3.htm