丨散列表中如何打造一個(gè)工業(yè)級(jí)水平的_第1頁(yè)
丨散列表中如何打造一個(gè)工業(yè)級(jí)水平的_第2頁(yè)
丨散列表中如何打造一個(gè)工業(yè)級(jí)水平的_第3頁(yè)
丨散列表中如何打造一個(gè)工業(yè)級(jí)水平的_第4頁(yè)
丨散列表中如何打造一個(gè)工業(yè)級(jí)水平的_第5頁(yè)
已閱讀5頁(yè),還剩16頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

今天,我們就來(lái)學(xué)下,如何設(shè)計(jì)一個(gè)可以應(yīng)對(duì)各種異常情況的工業(yè)級(jí)散列表,來(lái)避免在首先,散列函數(shù)的設(shè)計(jì)不能太復(fù)雜。過(guò)于復(fù)雜的散列函數(shù),勢(shì)必會(huì)消耗很多計(jì)算時(shí)間,也就間接的影響到散列表的性能。其次,散列函數(shù)生成的值要盡可能隨機(jī)并且均勻分布列,而且即便出現(xiàn),散列到每個(gè)槽里的數(shù)據(jù)也會(huì)比較平均,不會(huì)出現(xiàn)某個(gè)槽內(nèi)數(shù)據(jù)特別多的情況。實(shí)際工作中,我們還需要綜合考慮各種因素。這些因素有關(guān)鍵字的長(zhǎng)度、特點(diǎn)、分布、還有散列表的大小等。散列函數(shù)各式各樣,我舉幾個(gè)常用的、簡(jiǎn)單的散列函數(shù)的設(shè)計(jì)方法,讓你有個(gè)直觀的感受。第一個(gè)例子就是我們上一節(jié)的學(xué)生運(yùn)動(dòng)會(huì)的例子,我們通過(guò)分析參賽編號(hào)的特征,把編號(hào)中的后兩位作為散列值。我們還可以用類似的散列函數(shù)處理號(hào)碼,因?yàn)樘?hào)碼前幾位重復(fù)的可能性很大,但是后面幾位就比較隨機(jī),我們可以取號(hào)的后四位作為散列值。這種第二個(gè)例子就是上一節(jié)的開(kāi)篇思考題,如何實(shí)現(xiàn)Word數(shù),我們就可以這樣設(shè)計(jì):將單詞中每個(gè)字母的ACle,我們轉(zhuǎn)化出來(lái)的散列值就是下面1hash("nice")=(("n"-"a")*26*26*26+("i"-"a")*26*26+("c"-"a")*26+("e"-"a"))我們上一節(jié)講到散列表的裝載因子的時(shí)候,裝載因子越大,說(shuō)明散列表中的元素越多,空閑位置越少,散列的概率就越大。不僅插入數(shù)據(jù)的過(guò)程要多次尋址或者拉很長(zhǎng)的鏈,查找的過(guò)程也會(huì)因此變得很慢。列就會(huì)變得不可接受。這個(gè)時(shí)候,我們?cè)撊绾翁幚磲槍?duì)散列表,當(dāng)裝載因子過(guò)大時(shí),我們也可以進(jìn)行動(dòng)態(tài)擴(kuò)容,重新申請(qǐng)一個(gè)更大的散列表,將數(shù)據(jù)搬移到這個(gè)新散列表中。假設(shè)每次擴(kuò)容我們都申請(qǐng)一個(gè)原來(lái)散列表大小兩倍的空間。如果原來(lái)散列表的裝載因子是8,那經(jīng)過(guò)擴(kuò)容之后,新散列表的裝載因子就下降為原來(lái)的一半,變成了。針對(duì)數(shù)組的擴(kuò)容,數(shù)據(jù)搬移操作比較簡(jiǎn)單。但是,針對(duì)散列表的擴(kuò)容,數(shù)據(jù)搬移操作要復(fù)雜很多。因?yàn)樯⒘斜淼拇笮∽兞耍瑪?shù)據(jù)的位置也變了,所以我們需要通過(guò)散列函數(shù)重新計(jì)算每個(gè)數(shù)據(jù)的位置。你可以看我圖里這個(gè)例子。在原來(lái)的散列表中,21這個(gè)元素原來(lái)在下標(biāo)為0的位置,搬移到新的散列表中,在下標(biāo)為7的位置。對(duì)于支持動(dòng)態(tài)擴(kuò)容的散列表,插入操作的時(shí)間復(fù)雜度是多少呢?前面章節(jié)我已經(jīng)多次分析過(guò)支持動(dòng)態(tài)擴(kuò)容的數(shù)組、棧等數(shù)據(jù)結(jié)構(gòu)的時(shí)間復(fù)雜度了。所以,這里我就不啰嗦了,你要是還不清楚的話,可以回去復(fù)下。插入一個(gè)數(shù)據(jù),最好情況下,不需要擴(kuò)容,最好時(shí)間復(fù)雜度是O(1。情況下,散列表裝載因子過(guò)高,啟動(dòng)擴(kuò)容,我們需要重新申請(qǐng)內(nèi)存空間,重新計(jì)算哈希位置,并且搬移數(shù)據(jù),所以時(shí)間復(fù)雜度是O(nO(1當(dāng)然果我加在行效能夠多消點(diǎn)內(nèi)間,可以費(fèi)勁來(lái)縮容了。加負(fù)載因子的值,甚至可以大于1我們剛剛分析得到,大部分情況下,動(dòng)態(tài)擴(kuò)容的散列表插入一個(gè)數(shù)據(jù)都很快,但是在特殊情況下,當(dāng)裝載因子已經(jīng)到達(dá)閾值,需要先進(jìn)行擴(kuò)容,再插入數(shù)據(jù)。這個(gè)時(shí)候,插入數(shù)據(jù)就會(huì)變得很慢,甚至?xí)o(wú)法接受。我舉一個(gè)的例子,如果散列表當(dāng)前大小為1GB,要想擴(kuò)容為原來(lái)的兩倍大小,那就需要對(duì)1GB的數(shù)據(jù)重新計(jì)算哈希值,并且從原來(lái)的散列表搬移到新的散列表,聽(tīng)起來(lái)就很耗時(shí),如果我們的業(yè)務(wù)代碼直接服務(wù)于用戶,盡管大部分情況下,插入一個(gè)數(shù)據(jù)的操作都很快,但是,極個(gè)別非常慢的插入操作,也會(huì)讓用戶。這個(gè)時(shí)候,“”擴(kuò)容的機(jī)制就不合解決擴(kuò)時(shí)過(guò)情況們可擴(kuò)容穿插入操過(guò)程分批完成。當(dāng)裝載因子觸達(dá)閾值之后,我們只申請(qǐng)新空間,但并不將老的數(shù)據(jù)搬移到新散列表當(dāng)有新數(shù)據(jù)要插入時(shí),新數(shù)據(jù)插入新散列表中,并且從老的散列表中拿出一個(gè)數(shù)據(jù)放入到新散列表。每次插入一個(gè)數(shù)據(jù)到散列表,我們都重復(fù)上面的過(guò)程。經(jīng)過(guò)多次插入操作之后,老的散列表中的數(shù)據(jù)就一點(diǎn)一點(diǎn)全部搬移到新散列表中了。這樣沒(méi)有了集中的數(shù)據(jù)搬移,插入操作就都變得很快了。耗時(shí)過(guò)多的情況。這種實(shí)現(xiàn)方式,任何情況下,插入一個(gè)數(shù)據(jù)的時(shí)間復(fù)雜度都是O(1)。法在實(shí)際的軟件開(kāi)發(fā)中都非常常用。比如,Java中LinkedHashMap就采用了鏈表法解決,ThreadLocalMap是通過(guò)線性探測(cè)的開(kāi)放尋址法來(lái)解決。那你知道,這兩種沖用CPU緩存加快查詢速度。而且,這種方法實(shí)現(xiàn)的散列表,序列化起來(lái)比較簡(jiǎn)單。鏈表法上一節(jié)我們講到,用開(kāi)放尋址法解決的散列表,刪除數(shù)據(jù)的時(shí)候比較麻煩,需要特殊標(biāo)記已經(jīng)刪除掉的數(shù)據(jù)。而且,在開(kāi)放尋址法中,所有的數(shù)據(jù)都在一個(gè)數(shù)組中,比起鏈表法來(lái)說(shuō),的代價(jià)更高。所以,使用開(kāi)放尋址法解決的散列表,裝載因子的上限不能太大。這也導(dǎo)致這種方法比鏈表法更浪費(fèi)內(nèi)存空間。首先,鏈表法對(duì)內(nèi)存的利用率比開(kāi)放尋址法要高。因?yàn)殒湵斫Y(jié)點(diǎn)可以在需要的時(shí)候再創(chuàng)建,并不需要像開(kāi)放尋址法那樣事先申請(qǐng)好。實(shí)際上,這一點(diǎn)也是我們前面講過(guò)的鏈表優(yōu)于數(shù)組鏈表法比起開(kāi)放尋址法,對(duì)大裝載因子的度更高。開(kāi)放尋址法只能適用裝載因子小于1的情況。接近1時(shí),就可能會(huì)有大量的散列,導(dǎo)致大量的探測(cè)、再散列等,性能會(huì)下降很多。但是對(duì)于鏈表法來(lái)說(shuō),只要散列函數(shù)的值隨機(jī)均勻,即便裝載因子變成10,也就還記得我們之前在鏈表那一節(jié)講的嗎?鏈表因?yàn)橐羔槪詫?duì)于比較小的對(duì)象的存CPU(4個(gè)字節(jié)或者8個(gè)字節(jié)),實(shí)際上,我們對(duì)鏈表法稍加改造,可以實(shí)現(xiàn)一個(gè)更加高效的散列表。那就是,鏈表法中的鏈表改造為其他高效的動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),比如跳表、樹(shù)。這樣,即便出現(xiàn)散列,情況下,所有的數(shù)據(jù)都散列到同一個(gè)桶內(nèi),那最終成的散列表的查找時(shí)間也只不過(guò)是O(logn)。這樣也就有效避免了前面講到的散列碰撞。子,Java中的HashMap這樣一個(gè)工業(yè)級(jí)的散列表,來(lái)具體看下,這些技術(shù)是怎么應(yīng)用HashMap16,當(dāng)然這個(gè)默認(rèn)值是可以設(shè)置的,如果事先知道大概的數(shù)HashMap的性能。0.75HashMap0.75*capacity(capacity于是,在JDK1.8版本中,為了對(duì)HashMap做進(jìn)一步優(yōu)化,我們引入了樹(shù)。而當(dāng)鏈表點(diǎn),提高HashMap的性能。當(dāng)樹(shù)結(jié)點(diǎn)個(gè)數(shù)少于8個(gè)的時(shí)候,又會(huì)將樹(shù)轉(zhuǎn)化為鏈表。 inthash(Objectkey)inth=return(h^(h16))&(capitity-1);//capicity4其中,hashCode()JavahashcodeStringpublicinthashCode()2intvar1=3if(var1==0&&this.value.length>0)4char[]var2=5for(intvar3=0;var3<this.value.length;++var3)6var1=31*var1+7}8this.hash=9}return}關(guān)于散列函數(shù)、裝載因子、動(dòng)態(tài)擴(kuò)容策略,還有散列的解決辦法,我們前面都講過(guò)了,具體如何選擇,還要結(jié)合具體的業(yè)務(wù)場(chǎng)景、具體的業(yè)務(wù)數(shù)據(jù)來(lái)具體分析。不過(guò)只要我們朝這三個(gè)方向努力,就離設(shè)計(jì)出工業(yè)級(jí)的散列表不遠(yuǎn)了。上一節(jié)的內(nèi)容比較偏理論,今天的內(nèi)容側(cè)重實(shí)戰(zhàn)。我主要講了如何設(shè)計(jì)一個(gè)工業(yè)級(jí)的散列以及應(yīng)對(duì)異常,防在下,表的能嚴(yán)重分了列解決方法。關(guān)于散列函數(shù)的設(shè)計(jì),我們要盡可能讓散列后的值隨機(jī)且均勻分布,這樣會(huì)盡可能地減少散列,即便之后,分配到每個(gè)槽內(nèi)的數(shù)據(jù)也比較均勻。除此之外,散列函數(shù)的設(shè)計(jì)也不能太復(fù)雜,太復(fù)雜就會(huì)太耗時(shí)間,也會(huì)影響散列表的性能。關(guān)于散列解決方法的選擇,我對(duì)比了開(kāi)放尋址法和鏈表法兩種方法的優(yōu)劣和適應(yīng)的場(chǎng)如樹(shù),來(lái)避免散列表時(shí)間復(fù)雜度成O(n),抵御列碰撞攻擊。但是,對(duì)于小規(guī)模數(shù)據(jù)、裝載因子不高的散列表,比較適合用開(kāi)放尋址法。對(duì)于動(dòng)態(tài)散列表來(lái)說(shuō),不管我們?nèi)绾卧O(shè)計(jì)散列函數(shù),選擇什么樣的散列解決方法。隨著數(shù)據(jù)的不斷增加,散列表總會(huì)出現(xiàn)裝載因子過(guò)高的情況。這個(gè)時(shí)候,我們就需要啟動(dòng)動(dòng)態(tài)擴(kuò)?歸科技所有 不得售賣。頁(yè)面已增加防盜追蹤,將依法其上一 18|散列表(上):Word文檔中的單詞拼寫檢查功能是如何實(shí)現(xiàn)的下一 20|散列表(下):為什么散列表和鏈表經(jīng)常會(huì)一起使用寫寫

inthash(Objectkey)inth=return(h^(h16)&(capitity1)capicity}…作者回復(fù): 37能否每節(jié)講完都有個(gè)代碼的 可能會(huì)有同學(xué)對(duì)那個(gè)mod(capacity-1)有疑問(wèn)這個(gè)很正常,因?yàn)槿鄙偾爸妹枋鰲l件即僅有一位是1其余位為0減1后后續(xù)為為1當(dāng)前位為0做與運(yùn)算等于取后面的所有位的值比如capacity=8即 減為 如hascode=5即 =5拉 33 hash(Objectkey){int姜 16總結(jié):散列表(中

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論