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