高性能Python-字典和集合

當數(shù)據(jù)沒有明確的順序時,集合(sets)和字典(dictionaries)都是理想的數(shù)據(jù)結構侦锯,一個Key唯一對應一個存儲對象趾疚, Key可以是一個string,也可以是任意一個hashable的對象赞草。

字典和集合的插入和查詢的時間復雜度是O(1),需要額外的內存開銷來支持吆鹤,但是實際上厨疙,插入和查詢的時間取決于在用的hash函數(shù)。dictionaries是Key-Value的集合疑务,sets是一個Key的集合沾凄。

插入和查詢

dictionaries和sets通過使用hash table達到O(1)的插入和查詢效率。對于一個hash表暑始,我們必須搞清楚這段連續(xù)內存里都存放了什么搭独,當我們插入新數(shù)據(jù)時,會產(chǎn)生兩個屬性廊镜,一個是Key的hash值牙肝,另一個是這個hash值和其他值的比較,當一個數(shù)據(jù)插入嗤朴,Key首先被hashed和masked配椭,key被轉化為一個List的高效索引。這個mask來確保雹姊,key的hash值可以為任意的整數(shù)股缸,通過mask可以讓hash值縮小至分配的buckets的范圍內,不會越界吱雏,其實敦姻,一個mask就是一段可變的二進制值0b111, 0b11111,通過mask和hash值的位操作,截斷hash值歧杏。

例如镰惦,如果一個hash表只分配了8塊內存,只能存儲8個對象犬绒,一個key的hash值為28975旺入,最后的index為28975 & 0b111 = 7

當然,我們需要檢查被插入的hash值對應的這塊內存的狀態(tài),如果這塊內存為空茵瘾,我們將hash值插入列表礼华,把對象復制進內存塊,如果這塊內存為in use拗秘,內存塊里的值為value圣絮,那么該對象已經(jīng)被添加過了,直接return聘殖,如果晨雳,內存塊里的值不為value行瑞,那么奸腺,我們必須找到新的位置去存放該對象。

找到新位置的方法稱為:probing血久,Python的probing機制引入原始hash值的高位bits因素突照,來幫助避免未來的hash沖突。另外氧吐, 一個評判hash表對應的數(shù)據(jù)分布情況的好壞的標準稱為‘load factor’讹蘑,和hash函數(shù)的熵有關。

接下來筑舅,我們看一下Python字典如何根據(jù)一個Key來確定index:
可以參考Python的源代碼dictobject.c

def index_sequence(key, mask=0b111, PERTURB_SHIFT=5):
    perturb = hash(key)
    i = perturb & mask
    yield i
    while True:
        i = ((i << 2) + i + perturb + 1)
        perturb >>= PERTURB_SHIFT
        yield i & mask

上面的這個算法是linear probing的升級版座慰,也是Python現(xiàn)在的機制,linear probing只能處理hash值的最后幾位(比如mask為0b111翠拣,只能截斷最后三位版仔,意味著,當兩個key的最后三位是相同時误墓,不僅會得到相同的hash值蛮粮,而且,也會得到相同的index值谜慌,算法和計算順序無關的)然想,對比后,Python的算法是擾亂的(perturb)欣范,每次計算index時变泄,perturb都會變,也就是恼琼,相同的key的后幾位妨蛹,由于不同的計算順序,返回不同的index驳癌,這處理了hash沖突滑燃。
別忘了我們能這么做的原因是,我們存儲了原始的Key值颓鲜。當查詢時表窘,通過key的hash值得到index典予,對比index中的key值和被查詢的key值是否一致,如果一致返回對象乐严,如果不一致瘤袖,繼續(xù)查詢。

Resizing

當更多的元素被添加到hash表時昂验,hash表必須調整大小來適應捂敌。事實證明,當一個hash表只是2/3被利用時既琴,hash沖突的概率是可以接受的占婉。所以,當hash表的存儲達到這個點時甫恩,我們需要分配更大的hash表逆济,更多的內存塊被分配,mask也調整為新的長度的mask磺箕,所有舊hash表中的數(shù)據(jù)被重新添加到新hash表中奖慌,因為你mask改變了,這個操作需要重新計算所有的index松靡,所以简僧,調整一個大hash表的開銷是十分昂貴的

最小的dictionaries和sets的大小是8(參見Python源碼dictobject.c),也就是說雕欺,當你只存儲3個KeyValue時岛马,Python的字典也會分配8個內存塊。

每次resize時阅茶,當小于50000個原色時蛛枚,內存每次隨之遞增4倍,大于50000個時脸哀,遞增2倍蹦浦,也就是說,無論字典和集合內存儲多少個元素撞蜂,分配的內存只會是如下的size:8盲镶,32,128蝌诡,512溉贿,2048,8192浦旱,32768...還有很重要的一點不要忘了宇色,我們要保障hash表只有2/3的負載,來確保可以接受的hash碰撞率宣蠕。所以例隆,當一個字典有1039個元素時,必須至少分配1039 * 5 / 3 = 1731個buckets抢蚀,所以根據(jù)規(guī)則镀层,Python將分配2048個元素。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末皿曲,一起剝皮案震驚了整個濱河市唱逢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屋休,老刑警劉巖坞古,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異博投,居然都是意外死亡绸贡,警方通過查閱死者的電腦和手機盯蝴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門毅哗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捧挺,你說我怎么就攤上這事虑绵。” “怎么了闽烙?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵翅睛,是天一觀的道長。 經(jīng)常有香客問我黑竞,道長捕发,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任很魂,我火速辦了婚禮扎酷,結果婚禮上,老公的妹妹穿的比我還像新娘遏匆。我一直安慰自己法挨,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布幅聘。 她就那樣靜靜地躺著凡纳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帝蒿。 梳的紋絲不亂的頭發(fā)上荐糜,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音,去河邊找鬼暴氏。 笑死丛版,一個胖子當著我的面吹牛,可吹牛的內容都是我干的偏序。 我是一名探鬼主播页畦,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼研儒!你這毒婦竟也來了豫缨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤端朵,失蹤者是張志新(化名)和其女友劉穎好芭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冲呢,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡舍败,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了敬拓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邻薯。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖乘凸,靈堂內的尸體忽然破棺而出厕诡,到底是詐尸還是另有隱情,我是刑警寧澤营勤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布灵嫌,位于F島的核電站,受9級特大地震影響葛作,放射性物質發(fā)生泄漏寿羞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一赂蠢、第九天 我趴在偏房一處隱蔽的房頂上張望绪穆。 院中可真熱鬧,春花似錦客年、人聲如沸霞幅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽司恳。三九已至,卻和暖如春绍傲,著一層夾襖步出監(jiān)牢的瞬間扔傅,已是汗流浹背耍共。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留猎塞,地道東北人试读。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像荠耽,于是被迫代替她去往敵國和親钩骇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內容