哈希情史知多少

<p align="right">——日拱一卒,不期而至!</p>node

nio

簡介

hash是咱們工做中常常聽到的詞,好比哈希表、哈希函數、hashCode、HashTable、HashMap等等,那麼它們之間到底有怎樣的愛恨情仇呢?來一塊兒看一看吧~~程序員

數組

講哈希表以前,咱們先來看看數據結構的鼻祖——數組。編程

數組比較簡單,我就很少說了,你們都會都懂,見下圖。數組

array

數組的下標通常從0開始,依次日後存儲元素,查找元素也是同樣,只能從頭(或從尾)依次查找元素。數據結構

好比,要查找4這個元素,從頭開始查找的話須要查找3次,從尾的話也須要2次。分佈式

早期的哈希表

上面講了數組的缺點,查找某個元素只能從頭或者從尾依次查找元素,直到匹配爲止,它的均衡時間複雜是O(n)。函數

那麼,利用數組有沒有什麼方法能夠快速的查找元素呢?學習

聰明的程序員哥哥們想到一種方法,經過哈希函數計算元素的值,用這個值肯定元素在數組中的位置,這樣時間複雜度就能縮短到O(1)了。3d

好比,有5個元素分別爲三、五、四、1,把它們放入到數組以前先經過哈希函數計算位置,精確放置,而不是像簡單數組那樣依次放置元素。blog

假如,這裏申請的數組長度爲8,咱們能夠造這麼一個哈希函數爲hash(x) = x % 8,那麼最後的元素就變成了下圖這樣:

hash

這時候咱們再查找4這個元素,先算一下它的hash值爲hash(4) = 4 % 8 = 4,因此直接返回4號位置的元素就能夠了。

進化的哈希表

事情看着挺完美,可是,來了一個元素13,要插入的哈希表中,算了一下它的hash值爲hash(13) = 13 % 8 = 5,AUWC,它計算的位置也是5,但是5號已經被人先一步佔領了,怎麼辦呢?

這就是哈希衝突,本文來源於工從號彤哥讀源碼。

爲何會出現哈希衝突呢?

由於咱們申請的數組是有限長度的,把無限的數字映射到有限的數組上遲早會出現衝突,即多個元素映射到同一個位置上。

好吧,既然出現了哈希衝突,那麼咱們就要解決它,必須幹!

How to?

線性探測法

既然5號位置已經有主了,那我元素13認慫,我日後挪一位,我到6號位置去,這就是線性探測法,當出現衝突的時候依次日後挪直到找到空位置爲止。

hash

然鵝,又來了個新元素12,算得其hash值爲hash(12) = 12 % 8 = 4,我TMDRL,要日後移3次到7號位置纔有空位置,這就致使了插入元素的效率很低,查找也是同樣的道理,先定位的4號位置,發現不是我要找的人,再接着日後移,直到找到7號位置爲止。

二次探測法

使用線性探測法有個很大的弊端,衝突的元素每每會堆積在一塊兒,好比,12號放到7號位置,再來個14號同樣衝突,接着日後再數組結尾了,再從頭開始放到0號位置,你會發現衝突的元素有彙集現象,這就很不利於查找了,一樣不利於插入新的元素。

這時候又有聰明的程序員哥哥提出了新的想法——二次探測法,當出現衝突時,我不是日後一位一位這樣來找空位置,而是使用原來的hash值加上i的二次方來尋找,i依次從1,2,3...這樣,直到找到空位置爲止。

仍是以上面的爲例,插入12號元素,過程是這樣的,本文來源於工從號彤哥讀源碼:

hash

這樣就能很快地找到空位置放置新元素,並且不會出現衝突元素堆積的現象。

然鵝,又來了新元素20,你瞅瞅放哪?

AYMY,發現放哪都放不進去了。

研究代表,使用二次探測法的哈希表,當放置的元素超過一半時,就會出現新元素找不到位置的狀況。

因此又引出一個新的概念——擴容。

什麼是擴容?

已放置元素達到總容量的x時,就須要擴容了,這個x時又叫做擴容因子

很顯然,擴容因子越大越好,代表哈希表的空間利用率越高。

因此,很遺憾,二次探測法沒法知足咱們的目標,擴容因子過小了,只有0.5,一半的空間都是浪費的。

這時候又到了程序員哥哥們發揮他們聰明特性的時候了,通過996頭腦風暴後,又想出了一種新的哈希表實現方式——鏈表法。

鏈表法

不就是解決衝突嘛!出現衝突我就不往數組中去放了,我用一個鏈表把同一個數組下標位置的元素鏈接起來,這樣不就能夠充分利用空間了嘛,啊哈哈哈哈~~

hash

嘿嘿嘿嘿,完美△△。

真的完美嘛,我是一名黑客,我一直往裏面放*%8=4的元素,而後你就會發現幾乎全部的元素都跑到同一個鏈表中去了,MD,最後的結果就是你的哈希表退化成了單鏈表,查詢插入元素的效率都變成了O(n)。

此時,固然有辦法,擴容因子幹啥滴?

好比擴容因子設置爲1,當元素個數達到8個時,擴容成兩倍,一半的元素還在4號位置,一半的元素去到了12號位置,緩解了哈希表的壓力。

然鵝,依舊不是很完美,本文來源於工從號彤哥讀源碼。

聰明的程序員哥哥們此次開啓了一次長大9127的頭腦風暴,終於搞出了一種新的結構——鏈表樹法(固然,這個名字是彤哥起的)。

鏈表樹法

雖然上面的擴容在元素個數比較少的時候能解決一部分問題,總體的查找插入效率也不會過低,由於元素個數少嘛。

可是,黑客還在攻擊,元素個數還在持續增長,當增長到必定程度的時候,總會致使查找插入效率特別低。

因此,換個思路,既然鏈表的效率低,我把它升級一下,當鏈表長的時候升級成紅黑樹怎麼樣?

嗯,神舟行,我看行,說幹就幹。

hash

嗯,不錯不錯,媽媽不再怕我遭到黑客攻擊了。

因此,到這就結束了嗎?

你想多了,NM,每次擴容都要移動一半的元素好麼,這樣真的好麼好麼好麼?

程序員哥哥們太難了,此次通過了12127的頭腦風暴,終於想出個新玩意——一致性Hash。

一致性Hash

一致性Hash更多地是運用在分佈式系統中,好比說Redis集羣部署了四個節點,咱們把全部的hash值定義爲0~2^32個,每一個節點上放置四分之一的元素。

> 此處只爲舉例,實際Redis集羣的原理是這樣的,具體數值不是這樣的。

此時,假設須要給Redis增長一個節點,好比node5,放在node3和node4中間,這樣只須要把node3到node4中間的元素從node4移動到node5上面就好了,其它的元素保持不變。

這樣,就增長了擴容的速度,並且影響的元素比較少,大部分請求幾乎無感知。

hash

總結

怎麼樣,是否是很精彩?

想系統地學習更多編程姿式嘛,來個人工從號「彤哥讀源碼」一塊兒浪啊~

nio

相關文章
相關標籤/搜索