Redis源碼閱讀—數(shù)據(jù)結(jié)構(gòu)之字典 dict.c/dict.h

dict.c/dict.h


一比规、 dict的定義

??字典,是一種用于實(shí)現(xiàn)鍵值對(duì)(key-value pair)保存的抽象數(shù)據(jù)結(jié)構(gòu)拦英,通過(guò)字典蜒什,可以在單個(gè)鍵(key)與單個(gè)值(value)之間進(jìn)行關(guān)聯(lián)(或者說(shuō)是將鍵映射成值),而這些關(guān)聯(lián)的鍵與值即為鍵值對(duì)疤估。

??在字典中灾常,每一個(gè)鍵都是獨(dú)一無(wú)二的霎冯,所以程序可以在字典中通過(guò)鍵來(lái)對(duì)值,甚至是鍵值對(duì)進(jìn)行操作钞瀑。

??在一些高級(jí)編程語(yǔ)言中沈撞,字典經(jīng)常作為一種內(nèi)置的數(shù)據(jù)結(jié)構(gòu)出現(xiàn),但是可惜的是雕什,C語(yǔ)言并不在此列缠俺,所以 Redis 自己實(shí)現(xiàn)了字典這種數(shù)據(jù)結(jié)構(gòu)。

??在 Redis 中贷岸,字典使用哈希表作為底層實(shí)現(xiàn)壹士,而一個(gè)哈希表可以有多個(gè)哈希結(jié)點(diǎn),每一個(gè)哈希節(jié)點(diǎn)保存了一組鍵值對(duì)偿警。下圖是一個(gè)典型的字典:

字典示意圖

??下面依次是哈希節(jié)點(diǎn)躏救、哈希表、字典螟蒸、迭代器的實(shí)現(xiàn)盒使。

哈希節(jié)點(diǎn)(dictEntry )

哈希節(jié)點(diǎn),在 dict.h/dictEntry 中進(jìn)行了定義:


// 哈希表節(jié)點(diǎn)
typedef struct dictEntry {
    
    // 鍵
    void *key;

    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

    // 指向下一個(gè)哈希節(jié)點(diǎn)七嫌,構(gòu)成單向鏈表忠怖,用以解決鍵沖突(鏈地址法)
    struct dictEntry *next;

} dictEntry;

  • key 屬性用來(lái)保存鍵值對(duì)中的鍵,聯(lián)合體(union)v 屬性用來(lái)保存鍵值對(duì)中的值抄瑟。由于 v 屬性使用 union 關(guān)鍵字進(jìn)行定義凡泣,所以鍵值對(duì)中的值可以是一個(gè)指針,一個(gè) uint64_t 類型的整數(shù)皮假,或者是一個(gè) int64_t 類型的整數(shù)鞋拟。

  • * next 屬性是指向下一個(gè)哈希節(jié)點(diǎn)的指針,用來(lái)將多個(gè)哈希值相同的鍵值對(duì)連接起來(lái)惹资,構(gòu)成一個(gè)單向鏈表贺纲,用以解決鍵沖突(collision)即通過(guò)鏈地址法解決鍵沖突。

哈希表(dictht )

哈希表褪测,在 dict.h/dictht 中進(jìn)行了定義:


//哈希表
typedef struct dictht {
    
    // 哈希字節(jié)數(shù)組
    dictEntry **table;

    // 哈希表大小
    unsigned long size;
    
    // 哈希表大小掩碼猴誊,總是等于 size - 1
    unsigned long sizemask;

    // 該哈希表已有節(jié)點(diǎn)的數(shù)量
    unsigned long used;

} dictht;

  • table 屬性是一個(gè)數(shù)組,數(shù)組中的每一個(gè)元素都是一個(gè)指向 dict.h/dictEntry 結(jié)構(gòu)的指針侮措。

  • size 屬性記錄了哈希表的大小懈叹,也即是 table 數(shù)組的大小。

  • used 屬性記錄了哈希表已有結(jié)點(diǎn)分扎,即鍵值對(duì)的數(shù)量澄成。

  • sizemask 屬性的值總是等于 size-1 ,是用來(lái)和哈希值一起決定一個(gè)鍵應(yīng)該被放在 table 數(shù)組的哪一個(gè)索引上面的。

字典(dict)

字典墨状,在 dict.h/dict 中進(jìn)行了定義:


//字典
typedef struct dict {

    // 類型特定函數(shù)
    dictType *type;

    // 私有數(shù)據(jù)
    void *privdata;

    // 哈希表
    dictht ht[2];

    // rehash 索引
    // 當(dāng) rehash 不在進(jìn)行時(shí)卫漫,值為 -1
    int rehashidx;

    // 目前正在運(yùn)行的迭代器的數(shù)量
    int iterators;
    
} dict;

  • ht屬性:每一個(gè)字典(dict)都有兩個(gè)哈希表(dictht),用來(lái)實(shí)現(xiàn) 漸進(jìn)式 rehash 肾砂,保存在 ht 數(shù)組當(dāng)中列赎。并且,在字典當(dāng)中镐确,一般只會(huì)使用 ht[0] 哈希表粥谬,只有在對(duì) ht[0] 哈希表進(jìn)行 rehash 時(shí)才會(huì)用到 ht[1]。

  • rehashidx 屬性用來(lái)記錄 rehash 進(jìn)度辫塌,當(dāng) rehash 沒(méi)有在進(jìn)行的時(shí)候漏策,rehashidx = -1 。

  • iterators 屬性用來(lái)記錄目前正在運(yùn)行的迭代器的數(shù)量臼氨。

  • type 屬性和 privdata 屬性是用來(lái)針對(duì)不同類型的鍵值對(duì)掺喻,創(chuàng)建多態(tài)字典的。

    • type 屬性是一個(gè)指向 dictType 結(jié)構(gòu)的指針储矩,每個(gè) dictType 結(jié)構(gòu)保存了一組用于操縱特定類型鍵值對(duì)的函數(shù)感耙,而 Redis 會(huì)為不同用途的字典設(shè)置不同的類型特定函數(shù)。
    • privdata 屬性保存了要傳給那些類型特定函數(shù)的可選參數(shù)持隧。

其中即硼,字典類型特定函數(shù)在 dict.h/dictType 中進(jìn)行了定義:


// 字典類型特定函數(shù)
typedef struct dictType {

    // 計(jì)算哈希值的函數(shù)
    unsigned int (*hashFunction)(const void *key);

    // 復(fù)制鍵的函數(shù)
    void *(*keyDup)(void *privdata, const void *key);

    // 復(fù)制值的函數(shù)
    void *(*valDup)(void *privdata, const void *obj);

    // 對(duì)比鍵的函數(shù)
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);

    // 銷毀鍵的函數(shù)
    void (*keyDestructor)(void *privdata, void *key);
    
    // 銷毀值的函數(shù)
    void (*valDestructor)(void *privdata, void *obj);

} dictType;


/* 外部變量 Hash table types */
extern dictType dictTypeHeapStringCopyKey;
extern dictType dictTypeHeapStrings;
extern dictType dictTypeHeapStringCopyKeyValue;

字典迭代器(dictIterator )

字典迭代器,在 dict.h/dictIterator 中進(jìn)行了定義:


// 字典迭代器
typedef struct dictIterator {
        
    // 被迭代的字典
    dict *d;

    // table :正在被迭代的哈希表屡拨,值可以是 0 或 1只酥。
    // index :迭代器當(dāng)前所指向的哈希表的索引位置
    // safe :標(biāo)識(shí)這個(gè)迭代器是否安全
    int table, index, safe;

    // entry :當(dāng)前迭代到的哈希節(jié)點(diǎn)
    // nextEntry :當(dāng)前迭代到的哈希節(jié)點(diǎn)的下一個(gè)哈希節(jié)點(diǎn)
    dictEntry *entry, *nextEntry;

    long long fingerprint; /* unsafe iterator fingerprint for misuse detection */
    
} dictIterator;

  • d 屬性是一個(gè)指向 dict 結(jié)構(gòu)的指針,保存了當(dāng)前迭代器正在處理的字典呀狼。

  • table 屬性用來(lái)標(biāo)識(shí)當(dāng)前正在被迭代的哈希表裂允,即 dict 結(jié)構(gòu)中的 dictht 數(shù)組 ht 的下標(biāo),值可以為 0 或 1 哥艇。

  • index 屬性用來(lái)標(biāo)識(shí)迭代器當(dāng)前所指向的哈希表的索引位置绝编。

  • safe 屬性用來(lái)標(biāo)識(shí)這個(gè)迭代器是否是安全的迭代器,這對(duì)該迭代器后續(xù)能調(diào)用的 API 有影響貌踏。其中十饥,safe = 1 表示該迭代器可以在迭代時(shí)進(jìn)行增刪改等操作,對(duì)字典進(jìn)行修改祖乳,即調(diào)用 dictDelete逗堵、dictFind 等函數(shù),如若不然凡资,則只能使用 dictNext 遍歷函數(shù)砸捏,而不能對(duì)字典進(jìn)行修改。

  • entry 屬性是一個(gè)指向 dictEntry 結(jié)構(gòu)的指針隙赁,指向當(dāng)前迭代到的哈希節(jié)點(diǎn)垦藏。

  • nextEntry 屬性是一個(gè)指向 dictEntry 結(jié)構(gòu)的指針,指向當(dāng)前迭代到的哈希節(jié)點(diǎn)的下一個(gè)哈希節(jié)點(diǎn)伞访。這個(gè)屬性存在的原因是掂骏,在安全迭代器運(yùn)行時(shí),entry 指針?biāo)赶虻墓?jié)點(diǎn)可能會(huì)被修改厚掷,所以需要一個(gè)指針來(lái)保存下一節(jié)點(diǎn)的位置弟灼,來(lái)防止指針丟失。

  • fingerprint 屬性是字典的指紋冒黑,用于誤用檢測(cè)田绑,即在不安全的迭代器對(duì)字典進(jìn)行操作后,與操作前的指紋值進(jìn)行對(duì)比抡爹,若不同掩驱,則說(shuō)明在迭代過(guò)程中有對(duì)字典進(jìn)行增刪操作,而這些操作在不安全的迭代器中是不被允許的冬竟。

宏定義常量

在 dict.h 中欧穴,對(duì)一些對(duì)常量的定義:


/*
 * 字典的操作狀態(tài)
 */
// 操作成功
#define DICT_OK 0
// 操作失敗(或出錯(cuò))
#define DICT_ERR 1


/*
 * 哈希表的初始大小
 */
#define DICT_HT_INITIAL_SIZE     4

即:

  • DICT_OK:字典操作狀態(tài)泵殴,表示操作成功

  • DICT_ERR:字典操作狀態(tài)涮帘,表示操作失敗或出錯(cuò)

  • DICT_HT_INITIAL_SIZE:哈希表的初始大小,值為 4


二笑诅、 dict 的 API

dict 的函數(shù)實(shí)現(xiàn)分為兩種调缨,一種是通過(guò)宏定義的函數(shù),一種是在 dict.c 內(nèi)實(shí)現(xiàn)的函數(shù)吆你。

1. 宏定義函數(shù)

函數(shù) 作用 時(shí)間復(fù)雜度
DICT_NOTUSED 用于在字典的私有數(shù)據(jù)不使用時(shí)同蜻,讓編譯器忽略該私有數(shù)據(jù),避免編譯器錯(cuò)誤 O(1)
dictFreeVal 釋放字典中給定哈希節(jié)點(diǎn)的值 O(1)
dictSetVal 設(shè)置字典中給定哈希節(jié)點(diǎn)的值 O(1)
dictSetSignedIntegerVal 將一個(gè)有符號(hào)整數(shù)設(shè)置為給定哈希節(jié)點(diǎn)的值 O(1)
dictSetUnsignedIntegerVal 將一個(gè)無(wú)符號(hào)整數(shù)設(shè)置為給定哈希節(jié)點(diǎn)的值 O(1)
dictFreeKey 釋放字典中給定哈希節(jié)點(diǎn)的鍵 O(1)
dictSetKey 設(shè)置字典中給定哈希節(jié)點(diǎn)的鍵 O(1)
dictCompareKeys 比較兩個(gè)鍵 O(1)
dictHashKey 計(jì)算給定字典中給定鍵的哈希值 O(1)
dictGetKey 獲取給定哈希節(jié)點(diǎn)的鍵 O(1)
dictGetVal 獲取給定哈希節(jié)點(diǎn)的值 O(1)
dictGetSignedIntegerVal 獲取給定哈希節(jié)點(diǎn)的有符號(hào)整數(shù) O(1)
dictGetUnsignedIntegerVal 獲取給定哈希節(jié)點(diǎn)的無(wú)符號(hào)整數(shù) O(1)
dictSlots 獲取給定字典的大小早处,即 ht[0].size + ht[1].size O(1)
dictSize 獲取給定字典的已有節(jié)點(diǎn)數(shù)量湾蔓,即 ht[0].used+ ht[1].used O(1)
dictIsRehashing 判斷指定字典是否正在進(jìn)行 rehash O(1)

2. dict.c 中實(shí)現(xiàn)的函數(shù)

在 dict.c 中定義了幾個(gè)靜態(tài)變量:


// 表示字典是否啟用 rehash 
static int dict_can_resize = 1;

// 強(qiáng)制 rehash 的比率
static unsigned int dict_force_resize_ratio = 5;

// 哈希種子,用于 dictGenCaseHashFunction() 函數(shù)中
static uint32_t dict_hash_function_seed = 5381;

  • dict_can_resize
    ??表示字典是否啟用 rehash 砌梆,程序可以通過(guò)后續(xù)在API 實(shí)現(xiàn)提及的dictEnableResize() 和 dictDisableResize() 函數(shù)默责,來(lái)手動(dòng)地允許或阻止哈希表進(jìn)行 rehash。但是咸包,即使該值為零桃序,也不是所有的 rehash 都會(huì)被阻止,這種情況與下列的 dict_force_resize_ratio 變量有關(guān)烂瘫。

  • dict_force_resize_ratio
    ??字典強(qiáng)制 rehash 比率媒熊。當(dāng)某個(gè)哈希表的已有哈希節(jié)點(diǎn)的數(shù)量和字典大小之間的比率大于字典強(qiáng)制 rehash 比率奇适,即 size / used > dict_force_resize_ratio 時(shí),即使 dict_can_resize = 0 也會(huì)強(qiáng)制執(zhí)行 rehash芦鳍。

  • dict_hash_function_seed
    ??哈希種子嚷往,用于哈希函數(shù)之一的 dictGenCaseHashFunction() 函數(shù)中。

而在 dict.c 中實(shí)現(xiàn)的函數(shù)柠衅,有以下幾種類型:

(1)私有函數(shù)

函數(shù) 作用 時(shí)間復(fù)雜度
_dictExpandIfNeeded 根據(jù)需要皮仁,初始化字典的哈希表,或者對(duì)字典的現(xiàn)有哈希表進(jìn)行擴(kuò)展菲宴。
函數(shù)內(nèi)部是通過(guò)調(diào)用 dictExpand() 函數(shù)來(lái)實(shí)現(xiàn)的
O(N)
_dictNextPower 對(duì)字典的哈希表 ht[0] 的大小做冪級(jí)擴(kuò)展贷祈,即計(jì)算第一個(gè)大于等于 ht[0].size 的 2^N ,用作 rehash 操作需要的 ht[1] 的大小 O(1)
_dictKeyIndex 返回哈希表中可以將給定的 key 插入的索引位置喝峦。
如果哈希表正在進(jìn)行 rehash 势誊,那么返回的索引總是新的哈希表的索引,即 ht[1] 的索引谣蠢,因?yàn)樵?rehash 進(jìn)行的過(guò)程中键科,插入的新節(jié)點(diǎn)總是插入到 ht[1] 當(dāng)中的
O(N)
_dictInit 初始化字典 O(1)

(2)哈希函數(shù)

函數(shù) 作用 備注
dictIdentityHashFunction 直接使用 key 作為哈希值 無(wú)
dictIntHashFunction 對(duì)一個(gè)整數(shù)進(jìn)行哈希 Thomas Wang's 32 bit Mix Function
dictGenHashFunction 對(duì)字符串進(jìn)行哈希 MurmurHash2, by Austin Appleby
dictGenCaseHashFunction 對(duì)大小寫(xiě)不敏感的字符串進(jìn)行哈希 基于 DJB 哈希
dictSetHashFunctionSeed 設(shè)置哈希種子 dict_hash_function_seed
dictGetHashFunctionSeed 獲取哈希種子 dict_hash_function_seed

具體實(shí)現(xiàn)與分析日后單開(kāi)一篇進(jìn)行學(xué)習(xí)與分析。

(3)其余 API 實(shí)現(xiàn)

函數(shù) 作用 時(shí)間復(fù)雜度
_dictReset 對(duì)給定哈希表的各項(xiàng)屬性值進(jìn)行重置或初始化 O(1)
dictCreate 創(chuàng)建一個(gè)新的字典漩怎,內(nèi)部調(diào)用了 _dictInit() 函數(shù)用以實(shí)現(xiàn)字典的初始化 O(1)
dictResize 縮小給定的字典的負(fù)載因子勋颖,即 ht[0].used / ht[0].size,讓其比例接近于 1 : 1勋锤。
內(nèi)部通過(guò)調(diào)用dictExpand() 函數(shù)實(shí)現(xiàn)
O(N)
dictExpand 擴(kuò)展或創(chuàng)建一個(gè)新的哈希表
具體來(lái)說(shuō)饭玲,先創(chuàng)建一個(gè)新的哈希表,之后:
1. 如果給定的字典的 ht[0] 不存在叁执,那么將新的哈希表設(shè)置為 ht[0] 茄厘,即意味著創(chuàng)建一個(gè)新的哈希表
2. 如果給定的字典的 ht[0] 存在,那么將新的哈希表設(shè)置為 ht[1] 谈宛,并且將字典的 rehashidx 屬性設(shè)置為 0次哈,使程序可以開(kāi)始對(duì)字典進(jìn)行 rehash,即對(duì)已有哈希表進(jìn)行擴(kuò)展
O(N)
dictRehash 執(zhí)行 N 步漸進(jìn)式 rehash吆录,將給定字典的哈希表 ht[0] 當(dāng)中的哈希節(jié)點(diǎn)分 N 步 rehash 到哈希表 ht[1] 當(dāng)中窑滞。
返回 0,表示所有鍵都已經(jīng)從 ht[0] 遷移到了 ht[1] 當(dāng)中恢筝。
返回 1哀卫,表示仍有鍵需要從 ht[0] 遷移到 ht[1] 當(dāng)中。
需要注意的是撬槽,每步 rehash 都是以一個(gè)哈希表索引(桶)作為單位的此改,即每一步 rehash 之后,被 rehash 的桶里的所有節(jié)點(diǎn)都會(huì)被移動(dòng)到新哈希表
O(N)
timeInMilliseconds 獲取以毫秒為單位的 UNIX 時(shí)間戳 O(1)
dictRehashMilliseconds 在給定毫秒數(shù)內(nèi)侄柔,以 100 步為單位共啃,對(duì)字典進(jìn)行 rehash (調(diào)用 dictRehash() 函數(shù))占调,返回該函數(shù)調(diào)用期間的 rehash 步數(shù) O(N)
_dictRehashStep 在字典不存在安全迭代器的情況下,對(duì)字典進(jìn)行單步 rehash移剪。字典在有安全迭代器的情況下究珊,是不能進(jìn)行 rehash 的。
這個(gè)函數(shù)被多個(gè)查找挂滓、更新操作調(diào)用苦银,使得字典在被使用的同時(shí)進(jìn)行 rehash
O(1)
dictAdd 嘗試將給定鍵值對(duì)添加到字典中啸胧。
內(nèi)部調(diào)用了 dictAddRaw() 函數(shù)赶站,通過(guò)對(duì)調(diào)用該函數(shù)返回的哈希節(jié)點(diǎn)的 val 屬性賦值完成操作
O(N)
dictAddRaw 嘗試將鍵插入到字典中,是比 dictAdd() 函數(shù)更加底層的函數(shù)纺念。
如果鍵在字典中已經(jīng)存在贝椿,返回 NULL;否則陷谱,程序會(huì)創(chuàng)建新的哈希結(jié)點(diǎn)烙博,插入到字典相應(yīng)的索引位置,為該哈希節(jié)點(diǎn)的 key 屬性賦值烟逊,并返回該哈希節(jié)點(diǎn)渣窜,交由用戶為該哈希節(jié)點(diǎn)的 val 屬性賦值(即在 dictAdd() 函數(shù)中對(duì)該哈希節(jié)點(diǎn)的 val 屬性賦值)
O(N)
dictReplace 將給定的鍵值對(duì)添加到字典中,如果鍵已經(jīng)存在宪躯,那么對(duì)舊有的哈希節(jié)點(diǎn)的值進(jìn)行更新乔宿。
如果鍵值對(duì)是全新添加的,返回 1访雪;如果鍵值對(duì)是通過(guò)對(duì)原有的鍵值對(duì)更新得來(lái)的详瑞,返回 0
O(N)
dictReplaceRaw 查找函數(shù),根據(jù)給定的 key 查找哈希節(jié)點(diǎn)并返回臣缀。
如果給定的 key 已經(jīng)存在坝橡,返回包含該 key 的哈希節(jié)點(diǎn);如果給定的 key 不存在精置,那么調(diào)用 dictAddRaw() 函數(shù)將 key 添加到字典當(dāng)中计寇,再返回包含該 key 的哈希節(jié)點(diǎn)。
總之脂倦,該函數(shù)總是會(huì)返回包含給定 key 的哈希節(jié)點(diǎn)
O(N)
dictGenericDelete 從字典中刪除包含給定鍵的哈希節(jié)點(diǎn)饲常,通過(guò)函數(shù)的 nofree 參數(shù)可以選擇是否調(diào)用鍵和值的釋放函數(shù)來(lái)刪除鍵值 O(1)
dictDelete 從字典中刪除包含給定鍵的哈希節(jié)點(diǎn),并且調(diào)用鍵值的釋放函數(shù)來(lái)刪除鍵值 O(1)
dictDeleteNoFree 從字典中刪除包含給定鍵的哈希節(jié)點(diǎn)狼讨,并且不調(diào)用鍵值的釋放函數(shù)來(lái)刪除鍵值 O(1)
_dictClear 刪除哈希表上的所有節(jié)點(diǎn)贝淤,并重置哈希表的各項(xiàng)屬性 O(N)
dictRelease 刪除并釋放整個(gè)字典 O(N)
dictFind 獲取字典中包含鍵 key 的哈希節(jié)點(diǎn) O(1)
dictFetchValue 獲取包含給定鍵 key 的哈希節(jié)點(diǎn)的值 O(1)
dictFingerprint 獲取給定字典的指紋。
指紋是一個(gè) 64 位的數(shù)字政供,用于表示字典在某個(gè)時(shí)間的狀態(tài)播聪。指紋在創(chuàng)建和釋放一個(gè)不安全迭代器時(shí)獲取朽基,并且兩次獲取的指紋會(huì)進(jìn)行比對(duì),如果指紋不一致离陶,那么說(shuō)明在迭代過(guò)程中執(zhí)行了被禁止的操作
O(1)
dictGetIterator 創(chuàng)建并返回給定字典的不安全迭代器 O(1)
dictGetSafeIterator 創(chuàng)建并返回給定字典的安全迭代器 O(1)
dictNext 獲取迭代器指向的當(dāng)前節(jié)點(diǎn) O(1)
dictReleaseIterator 釋放給定字典迭代器 O(1)
dictGetRandomKey 隨機(jī)返回字典中任意一個(gè)節(jié)點(diǎn) O(N)
dictGetRandomKeys 隨機(jī)返回字典中多個(gè)節(jié)點(diǎn) O(N)
rev 反轉(zhuǎn)位稼虎,工具函數(shù) O(1)
dictScan 對(duì)字典中的哈希節(jié)點(diǎn)進(jìn)行迭代。
具體源碼與算法日后分析
O(N)
dictEmpty 清空字典中的兩個(gè)哈希表上的所有哈希節(jié)點(diǎn)招刨,并重置字典屬性 O(N)
dictEnableResize 開(kāi)啟自動(dòng) rehash霎俩,即將 dict_can_resize 設(shè)為 1 O(1)
dictDisableResize 關(guān)閉自動(dòng) rehash,即將 dict_can_resize 設(shè)為 0 O(1)

三沉眶、 dict 的特性

  1. 每個(gè)字典帶有兩個(gè)哈希表 ht[0] 與 ht[1]
    ??由于隨著操作的進(jìn)行打却,哈希表中保存的鍵值對(duì)數(shù)量會(huì)隨之不斷地變化,為了使哈希表的負(fù)載因子保持在一個(gè)合理的范圍谎倔,會(huì)對(duì)哈希表進(jìn)行相應(yīng)的擴(kuò)展或者收縮柳击,所以需要進(jìn)行 rehash 操作。
    ??所以每個(gè)字典都會(huì)帶有兩個(gè)哈希表 ht[0] 與 ht[1]片习,其中捌肴,ht[0] 用于在平時(shí)使用,而 ht[1] 用于在 rehash 進(jìn)行時(shí)使用:將 ht[0] 上的所有結(jié)點(diǎn) rehash 到 ht[1] 上藕咏。
    ??rehash状知,指的是對(duì)鍵的哈希值與索引值重新計(jì)算,然后將鍵值對(duì)放到 ht[1] 的指定位置孽查。
    ??在 rehash 進(jìn)行時(shí)饥悴,新添加的哈希節(jié)點(diǎn)會(huì)被添加到哈希表 ht[1] 當(dāng)中,確保哈希表 ht[0] 當(dāng)中的結(jié)點(diǎn)只減不增卦碾。當(dāng) ht[0] 表中的結(jié)點(diǎn)全部遷移到 ht[1] 表中后铺坞,會(huì)釋放 ht[0] 表,將 ht[1] 設(shè)置為 ht[0]洲胖,并重置 ht[1]济榨。

  2. 漸進(jìn)式 Rehash
    ??對(duì)于上述提到的 rehash 操作,并不是一次性全部遷移完成绿映,而是分多次擒滑、漸進(jìn)式地完成的,因?yàn)樵趯?shí)際環(huán)境中叉弦,哈希表中保存的節(jié)點(diǎn)實(shí)際上是非常多的丐一,如果一次性全部遷移的話,會(huì)對(duì)服務(wù)器的性能造成很大影響淹冰,甚至?xí)?dǎo)致服務(wù)器服務(wù)停止库车。
    ??具體來(lái)說(shuō)程拭,就是在對(duì)字典進(jìn)行添加宝惰、刪除、查找和更新操作時(shí),會(huì)在需要 rehash 操作時(shí)進(jìn)行單步 rehash荠呐,從而將龐大的工作量分?jǐn)傞_(kāi)來(lái)胀茵。

  3. 鍵沖突
    ??哈希表使用鏈地址法來(lái)解決鍵沖突红符,即被分配到同一索引的多個(gè)鍵值會(huì)拉成一個(gè)單向鏈表赞弥,正因?yàn)槿绱耍诓檎也僮鲿r(shí)難免會(huì)用到循環(huán)結(jié)構(gòu)阵漏,而新的哈希節(jié)點(diǎn)插入時(shí)也是采用頭插法來(lái)實(shí)現(xiàn)的驻民。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市履怯,隨后出現(xiàn)的幾起案子回还,更是在濱河造成了極大的恐慌,老刑警劉巖虑乖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件懦趋,死亡現(xiàn)場(chǎng)離奇詭異晾虑,居然都是意外死亡疹味,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)帜篇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)糙捺,“玉大人,你說(shuō)我怎么就攤上這事笙隙『榈疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵竟痰,是天一觀的道長(zhǎng)签钩。 經(jīng)常有香客問(wèn)我,道長(zhǎng)坏快,這世上最難降的妖魔是什么铅檩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮莽鸿,結(jié)果婚禮上昧旨,老公的妹妹穿的比我還像新娘。我一直安慰自己祥得,他們只是感情好兔沃,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著级及,像睡著了一般乒疏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饮焦,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天怕吴,我揣著相機(jī)與錄音入偷,去河邊找鬼。 笑死械哟,一個(gè)胖子當(dāng)著我的面吹牛疏之,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暇咆,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼锋爪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了爸业?” 一聲冷哼從身側(cè)響起其骄,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扯旷,沒(méi)想到半個(gè)月后拯爽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钧忽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年毯炮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耸黑。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桃煎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出大刊,到底是詐尸還是另有隱情为迈,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布缺菌,位于F島的核電站葫辐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏伴郁。R本人自食惡果不足惜耿战,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛾绎。 院中可真熱鬧昆箕,春花似錦、人聲如沸租冠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)顽爹。三九已至纤泵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捏题。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工玻褪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人公荧。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓带射,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親循狰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子窟社,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容