前言
程序員對哈希算法應該都不陌生,好比業界著名的MD五、SHA、CRC等等;在平常開發中咱們常常用一個Map來裝載一些具備(key,value)結構的數據,利用哈希算法O(1)的時間複雜度提升程序處理效率,除此以外,你還知道哈希算法的其餘應用場景嗎?java
1. 什麼是哈希算法?
瞭解哈希算法的應用場景前,咱們先看下散列(哈希)思想,散列就是把任意長度的輸入經過散列算法變換成固定長度的輸出,輸入稱爲Key(鍵),輸出爲Hash值,即散列值hash(key),散列算法即hash()函數(散列與哈希是對hash的不一樣翻譯);實際上存儲這些散列值的是一個數組,稱爲散列表,散列表用的是數組支持按照下標隨機訪問數據的特性,把數據值與數組下標按散列函數作的一一映射,從而實現O(1)的時間複雜度查詢;git
1.1 散列衝突
目前的哈希算法MD五、SHA、CRC等都沒法作到一個不一樣的key對應的散列值都不同的散列函數,即沒法避免出現不一樣的key映射到同一個值的狀況,即出現了散列衝突,並且,由於數組的存儲空間有限,也會加大散列衝突的機率。如何解決散列衝突?咱們經常使用的散列衝突解決方法有兩類:開放尋址法(open addressing) 和 鏈表法(chaining)。程序員
1.1.1 開放尋址法
經過線性探測的方法找到散列表中空閒位置,寫入hash值:算法
如圖,834313在hash表中散列到303432的位置上,出現了衝突,則順序遍歷hash表直到找到空閒位置寫入834313;當散列表中空閒位置很少的時候,散列衝突的機率就會大大增長,通常狀況下,咱們會盡量保證散列表中有必定比例的空閒槽位,此時,咱們用裝載因子來表示空閒位置的多少,計算公式是:散列表的裝載因子=填入表中的元素個數/散列表的長度。裝載因子越大,說明空閒位置越少,衝突越多,散列表的性能就會降低。數據庫
當數據量比較小,裝載因子小的時候,適合採用開放尋址法,這也是java中的ThreadLocalMap使用開放尋址法解決散列衝突的緣由。數組
1.1.2 鏈表法
鏈表法是一種更經常使用的散列衝突解決辦法,也更簡單。如圖:緩存
在散列表中,每一個桶/槽會對應一條鏈表,全部散列值相同的元素咱們都放到相同槽位對應的鏈表中;當散列衝突比較多時,鏈表的長度也會變長,查詢hash值須要遍歷鏈表,這時查詢效率就會從O(1)退化成O(n)。安全
這種解決散列衝突的處理方法比較適合大對象、大數據量的散列表,並且,支持更多的優化策略,好比使用紅黑樹代替鏈表;jdk1.8爲了對HashMap作進一步優化,引入了紅黑樹,當鏈表長度太長(默認超過8)時,鏈表就會轉換成紅黑樹,這時能夠利用紅黑樹快速增刪查改的特色,提升HashMap的性能,當紅黑樹節點個數小於8個時,又將紅黑樹轉化成爲鏈表,由於在數據量比較小的狀況下,紅黑樹要維護平衡,比起鏈表,性能上的優點並不明顯。服務器
2. 哈希算法的應用場景
2.1 安全加密
最經常使用於加密的哈希算法是MD5(MD5 Message-Digest Algorithm)和SHA(Secure Hash Algorithm 安全散列算法),利用hash的特色計算出來的hash值很難反向推導原始數據,從而達到加密的目的。網絡
以MD5爲例子,哈希值是固定的128位二進制串,最多能表示 2^128 個數據,這個數據已是天文數字了,散列衝突的機率要小於1/2^128,若是但願經過窮舉法來找到跟這個MD5相同的另外一個數據,那耗費的時間也應該是天文數字了,因此在有限的時間內哈希算法仍是很難被破解的,這也就達到了加密效果了。
2.2 數據校驗
利用Hash函數對數據敏感的特色,能夠用來校驗網絡傳輸過程當中的數據是否正確,防止被惡意串改。
2.3 散列函數
利用hash函數相對均勻分佈的特色,取hash值做爲數據存儲的位置值,讓數據均勻分佈在容器裏面。
2.4 負載均衡
經過hash算法,對客戶端id地址或者會話id進行計算hash值,將取得的哈希值與服務器列表的大小進行取模運算,最終獲得的值就是應該被路由到的服務器編號。
2.5 數據分片
假如咱們有1T的日誌文件,裏面記錄了用戶的搜索關鍵詞,咱們想要快速統計出每一個關鍵詞被搜索的次數,該怎麼作呢?數據量比較大,很難放到一臺機的內存中,即便放到一臺機子上,處理時間也會很長,針對這個問題,咱們能夠先對數據進行分片,而後採用多臺機器處理的方法,來提升處理速度。
具體的思路是:爲了提升處理速度,咱們用n臺機器並行處理。從搜索記錄的日誌文件中,依次獨處每一個搜索關鍵詞,並經過哈希函數計算哈希值,而後再跟n取模,最終獲得的值,就是應該被分配到的機器編號;這樣哈希值相同的搜索關鍵詞就被分配到了同一臺機器上,每一個機器會分別計算關鍵詞出現的次數,最後合併起來就是最終的結果。實際上,這裏的處理過程也是MapReduce的基本設計思想。
2.6 分佈式存儲
對於海量的數據須要緩存的狀況,一臺緩存機器確定是不夠的,因而,咱們就須要將數據分佈在多臺機器上。 這時,咱們能夠藉助前面的分片思想,即經過哈希算法對數據取哈希值,而後對機器個數取模,獲得應該存儲的緩存機器編號。
可是,若是數據增多,原來的10臺機器已沒法承受,須要擴容了,這時是若是全部數據都從新計算哈希值,而後從新搬移到正確的機器上,那就至關於全部的緩存數據一會兒都失效了,會穿透緩存回源到數據庫,這樣就可能發生雪崩效應,壓垮數據庫。爲了新增緩存機器不搬移全部的數據,一致性哈希算法就是比較好的選擇了,主要的思想是:假設咱們有kge機器,數據的哈希值範圍是[0,Max],咱們將整個範圍劃分紅m個小區間(m遠大於k),每一個機器負責m/k個小區間,當有新機器加入時,咱們就將某幾個小區間的數據,從原來的機器中搬移到新的機器中,這樣,即不用所有從新哈希、搬移數據,也保持了各個機器上數據量的平衡。
3. 寫在最後
實際上,哈希算法還有不少其餘的應用,好比git commit id等等,不少應用都來自於對算法的理解和擴展,也是基礎的數據結構和算法的價值體現,須要咱們在工做中慢慢理解和體會。
參考文檔: 《數據結構與算法之美》王爭