[iOS开发]iOS中的Hash
文章目錄
- 前言
- 關(guān)聯(lián)對(duì)象的底層原理
- weak的實(shí)現(xiàn)原理
- KVO的實(shí)現(xiàn)原理
- iOS App簽名的原理
- 對(duì)象引用計(jì)數(shù)存儲(chǔ)的位置
- Runloop與線程的存儲(chǔ)關(guān)系
- NSDictionary的原理
- 哈希表
- 哈希表定義
- 哈希表優(yōu)缺點(diǎn)
- 哈希查找步驟
- 哈希表的存儲(chǔ)過程
- 哈希表的實(shí)現(xiàn)
- 負(fù)載因子 = 總鍵值對(duì)數(shù)/數(shù)組的個(gè)數(shù)
- 哈希沖突的解決方法
- 開散列
- 閉散列
- 再哈希法
- 建立公共溢出區(qū)
- 開閉散列二者的比較
- 拉鏈法的優(yōu)點(diǎn):
- 拉鏈法的缺點(diǎn):
- 線性探測(cè)法的缺點(diǎn)
- NSDictionary
前言
天天聽安卓的同學(xué)整Hash,猛的發(fā)現(xiàn)iOS也有很多底層原理也是Hash來實(shí)現(xiàn)的,好好學(xué)一下。
iOS中也用到了很多Hash表。
關(guān)聯(lián)對(duì)象的底層原理
[iOS開發(fā)]Category、Extension和關(guān)聯(lián)對(duì)象
關(guān)聯(lián)對(duì)象采用的是HashMap嵌套HashMap的結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)的,簡(jiǎn)單來說就是根據(jù)對(duì)象從第一個(gè)HashMap中取出存儲(chǔ)所有關(guān)聯(lián)對(duì)象的第二個(gè)HashMap,然后根據(jù)屬性名從第二個(gè)HashMap中取出屬性對(duì)應(yīng)的值和策略。
問題:為什么關(guān)聯(lián)對(duì)象沒有weak屬性?
weak的實(shí)現(xiàn)原理
weak底層原理
weak采用的是一個(gè)全局的HashMap嵌套數(shù)組的結(jié)構(gòu)存儲(chǔ)數(shù)據(jù)的。銷毀對(duì)象(weak指針指向的對(duì)象)的時(shí)候,根據(jù)對(duì)象從HashMap中找到存放所有指向該對(duì)象的weak指針的數(shù)組,然后將數(shù)組中的所有元素(weak指針)都置為nil
weak最大的特點(diǎn)就是在對(duì)象銷毀的時(shí)候,自動(dòng)置nil,減少訪問野指針的風(fēng)險(xiǎn),這也是設(shè)計(jì)weak的初衷。weak指針置nil的基本步驟:
- 對(duì)象dealloc的時(shí)候,從全局的HashMap中,根據(jù)一個(gè)唯一代碼對(duì)象的值作為key,找到存儲(chǔ)所有指向該對(duì)象的weak指針的數(shù)組
- 將數(shù)組中的所有元素都置nil
蘋果對(duì)于weak的實(shí)現(xiàn)其實(shí)類似于通知的實(shí)現(xiàn),指明誰(weak指針)要監(jiān)聽誰(賦值對(duì)象)什么事件(dealloc操作)執(zhí)行什么操作(置nil)
KVO的實(shí)現(xiàn)原理
GNUstep KVC/KVO探索(二):KVO的內(nèi)部實(shí)現(xiàn)
iOS App簽名的原理
一致性Hash算法 + 非對(duì)稱加密算法
iOS App 簽名的原理
對(duì)象引用計(jì)數(shù)存儲(chǔ)的位置
if 對(duì)象支持TaggedPointer {return 直接將對(duì)象的指針值作為引用計(jì)數(shù)返回 } else if 設(shè)備是64位環(huán)境 && Objective-C2.0 {return 對(duì)象isa指針的一部分空間(bits_extra_rc) } else {return hash表 }Runloop與線程的存儲(chǔ)關(guān)系
線程和Runloop之間是一一對(duì)應(yīng)的關(guān)系,其關(guān)系是保存在一個(gè)全局的Dictionary里。
線程在剛創(chuàng)建是沒有RunLoop,如果我們不主動(dòng)獲取,那他就一直都不會(huì)有。RunLoop的創(chuàng)建是發(fā)生在第一次獲取時(shí),RunLoop的銷毀時(shí)發(fā)生在線程結(jié)束時(shí)。我們只能在一個(gè)線程內(nèi)部獲取其RunLoop。
NSDictionary的原理
學(xué)完Hash之后我們來學(xué)習(xí)具體的實(shí)現(xiàn)
哈希表
前面說了那么多的底層原理都與Hash表有關(guān),我們來詳細(xì)學(xué)一下哈希表
哈希表定義
哈希表(hash tabl,也叫散列表),是根據(jù)鍵(key)直接訪問在內(nèi)存存儲(chǔ)位置的數(shù)據(jù)結(jié)構(gòu)。哈希表本質(zhì)是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素成為一個(gè)箱子,箱子中存放的是鍵值對(duì)。根據(jù)下標(biāo)index從數(shù)組中取value。關(guān)鍵是如何獲取index,這就需要一個(gè)固定的函數(shù)(哈希函數(shù)),將key轉(zhuǎn)換成index。不論哈希函數(shù)設(shè)計(jì)的如何完美,都可能出現(xiàn)不同的key經(jīng)過hash處理后得到相同的hash值,這時(shí)候我們就需要處理哈希沖突。
哈希表優(yōu)缺點(diǎn)
優(yōu)點(diǎn):把數(shù)據(jù)的存儲(chǔ)和查找消耗的時(shí)間大大降低,幾乎可以看成是常數(shù)時(shí)間;而代價(jià)僅僅是消耗比較多的內(nèi)存。然而在當(dāng)前可利用內(nèi)存越來越多的情況下,用空間換時(shí)間的做法是值得的。另外,編碼比較容易也是它的特點(diǎn)之一。
缺點(diǎn):哈希表通常是基于數(shù)組,數(shù)組創(chuàng)建后難于擴(kuò)展。也沒有一種簡(jiǎn)便的方法可以以任何一種順序來遍歷哈希表。
所以,如果不需要有序遍歷數(shù)據(jù),并且可以提前預(yù)測(cè)數(shù)據(jù)量的大小,那么哈希表在速度和易用性方面是無與倫比的。
哈希查找步驟
哈希表的存儲(chǔ)過程
哈希表的實(shí)現(xiàn)
哈希表的底層實(shí)際上是基于數(shù)組來存儲(chǔ)的,當(dāng)插入鍵值對(duì)時(shí),并不是直接插入該數(shù)組中,而是通過對(duì)鍵進(jìn)行Hash運(yùn)算得到Hash值,然后和數(shù)組容量取模,得到在數(shù)組中的位置后再插入。取值時(shí),先對(duì)指定的鍵求Hash值,再和容量取模得到底層數(shù)組中對(duì)應(yīng)的位置,如果指定的鍵值與存貯的鍵相匹配,則返回該鍵值對(duì),如果不匹配,則表示哈希表中沒有對(duì)應(yīng)的鍵值對(duì)。這樣做的好處是在查找、插入、刪除等操作可以做到 O ( 1 ) O(1) O(1),最壞的情況是 O ( n ) O(n) O(n),當(dāng)然這種是最極端的情況,極少遇到。
負(fù)載因子 = 總鍵值對(duì)數(shù)/數(shù)組的個(gè)數(shù)
負(fù)載因子是哈希表的一個(gè)重要屬性,用來衡量哈希表的空/滿成都,一定程度也可以體現(xiàn)查詢的效率。負(fù)載因子越大,意味著哈希表越滿,越容易導(dǎo)致沖突,性能也就越低。所以當(dāng)負(fù)載因子大于某個(gè)常數(shù)(一般是0.75)時(shí),哈希表將自動(dòng)擴(kuò)容。哈希表擴(kuò)容是,一般會(huì)創(chuàng)建兩倍于原來的數(shù)組長(zhǎng)度。這個(gè)過程被稱為重哈希(rehash)。
哈希表擴(kuò)容在數(shù)組比較多的時(shí)候需要重新哈希并移動(dòng)數(shù)據(jù),性能影響較大。
哈希表擴(kuò)容雖然能夠使負(fù)載因子降低,但并不總能有效提高哈希表的查詢性能。比如哈希函數(shù)設(shè)計(jì)的不合理,導(dǎo)致所有的key計(jì)算出的哈希值都相同,那么即使擴(kuò)容他們的位置還是在同一條鏈表上,變成了線性表,性能也極低,查詢時(shí)候的時(shí)間復(fù)雜度就變成了O(n)。
哈希沖突的解決方法
大類分為四種 開散列、閉散列、再哈希法、建立公共溢出區(qū)
開散列
開散列也叫鏈地址法,也叫拉鏈法、哈希桶。
簡(jiǎn)單來說就是 數(shù)組 + 鏈表。將鍵通過hash函數(shù)映射為大小為M的數(shù)組的下標(biāo)索引,數(shù)組的每一個(gè)元素指向一個(gè)鏈表,鏈表中的每一個(gè)節(jié)點(diǎn)都存儲(chǔ)著hash出來的索引值為結(jié)點(diǎn)下標(biāo)的鍵值對(duì)。
JAVA 8解決哈希沖突采用的就是拉鏈法。在處理哈希函數(shù)設(shè)計(jì)不合理導(dǎo)致鏈表很長(zhǎng)時(shí)(鏈表長(zhǎng)度超過8切換為紅黑樹,小于6重新退化為鏈表)。將鏈表切換為紅黑樹能夠保證插入和查找的效率,缺點(diǎn)是當(dāng)哈希表比較大時(shí),哈希表擴(kuò)容會(huì)導(dǎo)致瞬時(shí)效率降低。
Redis解決哈希沖突采用的也是拉鏈法。通過增量式擴(kuò)容解決了java 8中的瞬時(shí)擴(kuò)容導(dǎo)致的瞬時(shí)效率降低的缺點(diǎn),同時(shí)拉鏈法的實(shí)現(xiàn)方式(新插入的鍵值對(duì)放在鏈表頭部)帶來了兩個(gè)好處:
一、 頭插法可以節(jié)省插入耗時(shí)。如果插到尾部,則需要時(shí)間復(fù)雜度為O(n)的操作找到鏈表的尾部,或者需要額外的內(nèi)存地址來保存尾部鏈表的位置。
二、頭插法可以節(jié)省查找耗時(shí)。最新插入的數(shù)據(jù)往往可能頻繁的被查詢。
閉散列
閉散列也叫開放地址法、線性探測(cè)法。
當(dāng)發(fā)生哈希沖突時(shí),且哈希表未滿,可以通過探測(cè)的方法吧key存放在沖突位置的下一個(gè)位置中。
方法一:線性探測(cè)
從發(fā)生沖突的位置開始,一次向后探測(cè),直到尋找到下一個(gè)空位置為止。當(dāng)然這種方法比較簡(jiǎn)單,也有很大的缺陷,就是容易造成數(shù)據(jù)的堆積,使得關(guān)鍵字需要多次比較,這樣以來就導(dǎo)致搜索效率低
方法二:二次探測(cè)
方法三:偽隨機(jī)探測(cè)
建立一個(gè)偽隨機(jī)數(shù)發(fā)生器(如 i = (i + p) % m),生成一個(gè)偽隨機(jī)序列,并給定一個(gè)隨機(jī)數(shù)做起點(diǎn),每次加上這個(gè)偽隨機(jī)數(shù),++就可以了。
再哈希法
再哈希法其實(shí)很簡(jiǎn)單,就是再使用哈希函數(shù)去散列一個(gè)輸入的時(shí)候,輸出是同一個(gè)位置就再次哈希,直至不發(fā)生沖突位置
缺點(diǎn):每次沖突都要重新哈希,計(jì)算時(shí)間增加。
建立公共溢出區(qū)
這種方法的基本思想是:將哈希表分為基本表和溢出表兩部分,凡是和基本表發(fā)生沖突的元素,一律填入溢出表。
開閉散列二者的比較
拉鏈法的優(yōu)點(diǎn):
與線性探測(cè)法相比,拉鏈法有如下幾個(gè)優(yōu)點(diǎn):
- 處理沖突簡(jiǎn)單,且無堆積現(xiàn)象,即非同義詞絕不會(huì)發(fā)生沖突,因此平均查找長(zhǎng)度較短;
- 由于拉鏈法中各鏈表上的結(jié)點(diǎn)空間是動(dòng)態(tài)申請(qǐng)的,所以更適合于造表錢無法確定表長(zhǎng)的情況
- 線性探測(cè)法為減少?zèng)_突,要求裝填因子較小,故當(dāng)結(jié)點(diǎn)規(guī)模較大時(shí)會(huì)浪費(fèi)很多空間。二拉鏈法中可取a>=1,且結(jié)點(diǎn)較大時(shí),拉鏈法中增加的指針域可忽略不計(jì),因此節(jié)省空間
- 在用拉鏈法構(gòu)造的散列表中,刪除結(jié)點(diǎn)的操作易于實(shí)現(xiàn)。只要簡(jiǎn)單地刪去鏈表上相應(yīng)的結(jié)點(diǎn)即可。而對(duì)開放定址線性探測(cè)發(fā)構(gòu)造的散列表,刪除結(jié)點(diǎn)不能簡(jiǎn)單地將被刪結(jié) 點(diǎn)的空間置為空,否則將截?cái)嘣谒筇钊松⒘斜淼耐x詞結(jié)點(diǎn)的查找路徑。這是因?yàn)楦鞣N開放定址線性探測(cè)發(fā)中,空地址單元(即開放地址)都是查找失敗的條件。因此在用開放定址線性探測(cè)發(fā)處理沖突的散列表上執(zhí)行刪除操作,只能在被刪結(jié)點(diǎn)上做刪除標(biāo)記,而不能真正刪除結(jié)點(diǎn)。
拉鏈法的缺點(diǎn):
指針需要額外的空間,故當(dāng)結(jié)點(diǎn)規(guī)模較小時(shí),開放定址線性探測(cè)法較為節(jié)省空間,而若將節(jié)省的指針空間用來擴(kuò)大散列表的規(guī)模,可使裝填因子變小,這又減少了開放定址線性探測(cè)法中的沖突,從而提高平均查找速度。
線性探測(cè)法的缺點(diǎn)
NSDictionary
總結(jié)
以上是生活随笔為你收集整理的[iOS开发]iOS中的Hash的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10岁男童高考566分8岁开发操作系统
- 下一篇: 当“互联网+”遇上“新零售”,卖1000