主要函數(shù)
//返回字典中包含鍵 key 的節(jié)點,找到返回節(jié)點,找不到返回 NULL
dictEntry *dictFind(dict *d, const void *key)
// 如果給定key不存在,將key插入到字典,如果key存在,返回該key的字典節(jié)點
dictEntry *dictReplaceRaw(dict *d, void *key)
//查找并刪除包含給定鍵的節(jié)點,參數(shù) nofree 決定是否調(diào)用鍵和值的釋放函數(shù)。0 表示調(diào)用,1 表示不調(diào)用
static int dictGenericDelete(dict *d, const void *key, int nofree)
//從字典中刪除包含給定鍵的節(jié)點,并且調(diào)用鍵值的釋放函數(shù)來刪除鍵值
int dictDelete(dict *ht, const void *key)
//從字典中刪除包含給定鍵的節(jié)點,但不調(diào)用鍵值的釋放函數(shù)來刪除鍵值
int dictDeleteNoFree(dict *ht, const void *key)
//刪除哈希表上的所有節(jié)點,并重置哈希表的各項屬性
int _dictClear(dict *d, dictht *ht, void(callback)(void *))
//刪除并釋放整個字典
void dictRelease(dict *d)
//清空字典上的所有哈希表節(jié)點,并重置字典屬性
void dictEmpty(dict *d, void(callback)(void*))
//獲取包含給定鍵的節(jié)點的值
void *dictFetchValue(dict *d, const void *key)
//隨機返回字典中任意一個節(jié)點??捎糜趯崿F(xiàn)隨機化算法。
dictEntry *dictGetRandomKey(dict *d)
//返回連續(xù)的count個節(jié)點,開頭的節(jié)點隨機,要保證des有充足的空間
int dictGetRandomKeys(dict *d, dictEntry **des, int count)
dictFind
返回字典中包含鍵 key 的節(jié)點,找到返回節(jié)點,找不到返回 NULL
dictEntry *dictFind(dict *d, const void *key)
{
dictEntry *he;
unsigned int h, idx, table;
// 字典(的哈希表)為空
if (d->ht[0].size == 0) return NULL;
// 如果條件允許的話,進行單步 rehash
if (dictIsRehashing(d)) _dictRehashStep(d);
// 計算鍵的哈希值
h = dictHashKey(d, key);
// 在字典的哈希表中查找這個鍵
// T = O(1)
for (table = 0; table <= 1; table++) {
// 計算索引值
idx = h & d->ht[table].sizemask;
// 遍歷給定索引上的鏈表的所有節(jié)點,查找 key
he = d->ht[table].table[idx];
// T = O(1)
while(he) {
if (dictCompareKeys(d, key, he->key))
return he;
he = he->next;
}
// 如果程序遍歷完 0 號哈希表,仍然沒找到指定的鍵的節(jié)點
// 那么程序會檢查字典是否在進行 rehash ,
// 如果正在rehash,繼續(xù)查找1號哈希表,否則返回NULL
if (!dictIsRehashing(d)) return NULL;
}
// 進行到這里時,說明兩個哈希表都沒找到
return NULL;
}
dictReplace
將給定的鍵值對添加到字典中,如果鍵已經(jīng)存在,那么刪除舊有的鍵值對。
如果鍵值對為全新添加,那么返回 1 。
如果鍵值對是通過對原有的鍵值對更新得來的,那么返回 0 。
T = O(N)
int dictReplace(dict *d, void *key, void *val)
{
dictEntry *entry, auxentry;
// 嘗試直接將鍵值對添加到字典
// 如果鍵 key 不存在的話,添加會成功
// T = O(N)
if (dictAdd(d, key, val) == DICT_OK)
return 1;
// 運行到這里,說明鍵 key 已經(jīng)存在,那么找出包含這個 key 的節(jié)點
// T = O(1)
entry = dictFind(d, key);
// 先保存原有的值的指針
auxentry = *entry;
// 然后設置新的值
// T = O(1)
dictSetVal(d, entry, val);
// 然后釋放舊值
// T = O(1)
dictFreeVal(d, &auxentry);
return 0;
}
dictReplaceRaw
如果給定key不存在,將key插入到字典,如果key存在,返回該key的字典節(jié)點
dictEntry *dictReplaceRaw(dict *d, void *key) {
// 使用 key 在字典中查找節(jié)點
// T = O(1)
dictEntry *entry = dictFind(d,key);
// 如果節(jié)點找到了直接返回節(jié)點,否則添加并返回一個新節(jié)點
// T = O(N)
return entry ? entry : dictAddRaw(d,key);
}
dictGenericDelete
靜態(tài)函數(shù),查找并刪除包含給定鍵的節(jié)點
參數(shù) nofree 決定是否調(diào)用鍵和值的釋放函數(shù),0 表示調(diào)用,1 表示不調(diào)用
找到并成功刪除返回 DICT_OK ,沒找到則返回 DICT_ERR
T = O(1)
static int dictGenericDelete(dict *d, const void *key, int nofree)
{
unsigned int h, idx;
dictEntry *he, *prevHe;
int table;
// 字典(的哈希表)為空
if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
// 進行單步 rehash ,T = O(1)
if (dictIsRehashing(d)) _dictRehashStep(d);
// 計算哈希值
h = dictHashKey(d, key);
// 遍歷哈希表
// T = O(1)
for (table = 0; table <= 1; table++) {
// 計算索引值
idx = h & d->ht[table].sizemask;
// 指向該索引上的鏈表
he = d->ht[table].table[idx];
prevHe = NULL;
// 遍歷鏈表上的所有節(jié)點
// T = O(1)
while(he) {
if (dictCompareKeys(d, key, he->key)) {
// 超找目標節(jié)點
/* Unlink the element from the list */
// 從鏈表中刪除
if (prevHe)
prevHe->next = he->next;
else
d->ht[table].table[idx] = he->next;
// 釋放調(diào)用鍵和值的釋放函數(shù)?
if (!nofree) {
dictFreeKey(d, he);
dictFreeVal(d, he);
}
// 釋放節(jié)點本身
zfree(he);
// 更新已使用節(jié)點數(shù)量
d->ht[table].used--;
// 返回已找到信號
return DICT_OK;
}
prevHe = he;
he = he->next;
}
// 如果執(zhí)行到這里,說明在 0 號哈希表中找不到給定鍵
// 那么根據(jù)字典是否正在進行 rehash ,決定要不要查找 1 號哈希表
if (!dictIsRehashing(d)) break;
}
// 沒找到
return DICT_ERR; /* not found */
}
dictDelete
從字典中刪除包含給定鍵的節(jié)點,并且調(diào)用鍵值的釋放函數(shù)來刪除鍵值
int dictDelete(dict *ht, const void *key) {
return dictGenericDelete(ht,key,0);
}
dictDeleteNoFree
從字典中刪除包含給定鍵的節(jié)點,但不調(diào)用鍵值的釋放函數(shù)來刪除鍵值
int dictDeleteNoFree(dict *ht, const void *key) {
return dictGenericDelete(ht,key,1);
}
_dictClear
刪除哈希表上的所有節(jié)點,并重置哈希表的各項屬性
T = O(N)
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
unsigned long i;
// 遍歷整個哈希表
// T = O(N)
for (i = 0; i < ht->size && ht->used > 0; i++) {
dictEntry *he, *nextHe;
if (callback && (i & 65535) == 0) callback(d->privdata);
// 跳過空索引
if ((he = ht->table[i]) == NULL) continue;
// 遍歷整個鏈表
// T = O(1)
while(he) {
nextHe = he->next;
// 刪除鍵
dictFreeKey(d, he);
// 刪除值
dictFreeVal(d, he);
// 釋放節(jié)點
zfree(he);
// 更新已使用節(jié)點計數(shù)
ht->used--;
// 處理下個節(jié)點
he = nextHe;
}
}
// 釋放哈希表結構
zfree(ht->table);
// 重置哈希表屬性
_dictReset(ht);
return DICT_OK; /* never fails */
}
dictRelease
刪除并釋放整個字典
T = O(N)
void dictRelease(dict *d)
{
// 刪除并清空兩個哈希表
_dictClear(d,&d->ht[0],NULL);
_dictClear(d,&d->ht[1],NULL);
// 釋放節(jié)點結構
zfree(d);
}
dictEmpty
清空字典上的所有哈希表節(jié)點,并重置字典屬性
void dictEmpty(dict *d, void(callback)(void*)) {
// 刪除兩個哈希表上的所有節(jié)點
// T = O(N)
_dictClear(d,&d->ht[0],callback);
_dictClear(d,&d->ht[1],callback);
// 重置屬性
d->rehashidx = -1;
d->iterators = 0;
}
dictFetchValue
獲取包含給定鍵的節(jié)點的值
如果節(jié)點不為空,返回節(jié)點的值,否則返回 NULL
T = O(1)
void *dictFetchValue(dict *d, const void *key) {
dictEntry *he;
// T = O(1)
he = dictFind(d,key);
return he ? dictGetVal(he) : NULL;
}
dictGetRandomKey
隨機返回字典中任意一個節(jié)點。可用于實現(xiàn)隨機化算法。如果字典為空,返回 NULL 。
T = O(N)
dictEntry *dictGetRandomKey(dict *d)
{
dictEntry *he, *orighe;
unsigned int h;
int listlen, listele;
// 字典為空
if (dictSize(d) == 0) return NULL;
// 進行單步 rehash
if (dictIsRehashing(d)) _dictRehashStep(d);
// 如果正在 rehash ,那么將 1 號哈希表也作為隨機查找的目標
if (dictIsRehashing(d)) {
// T = O(N)
do {
//隨機取一個索引,范圍在兩個哈希表的size相加的區(qū)間內(nèi),相當于將兩個哈希表的table拼接起來,ht[0]作為前半段,ht[1]作為后半段
h = random() % (d->ht[0].size+d->ht[1].size);
he = (h >= d->ht[0].size) ? d->ht[1].table[h - d->ht[0].size] :
d->ht[0].table[h];
//該索引下可能沒有節(jié)點,所以要繼續(xù)循環(huán)找,直到找到有節(jié)點的索引
} while(he == NULL);
// 否則,只從 0 號哈希表中查找節(jié)點
} else {
// T = O(N)
do {
h = random() & d->ht[0].sizemask;
he = d->ht[0].table[h];
} while(he == NULL);
}
// 目前 he 已經(jīng)指向一個非空的節(jié)點鏈表
// 程序將從這個鏈表隨機返回一個節(jié)點
listlen = 0;
orighe = he;
// 計算節(jié)點數(shù)量, T = O(1)
while(he) {
he = he->next;
listlen++;
}
// 取模,得出隨機節(jié)點的索引
listele = random() % listlen;
he = orighe;
// 按索引查找節(jié)點
// T = O(1)
while(listele--) he = he->next;
// 返回隨機節(jié)點
return he;
}
dictGetRandomKeys
返回count個連續(xù)的節(jié)點,第一個節(jié)點隨機取,然后直接取第一個節(jié)點后面的count -1 個,函數(shù)調(diào)用時,要保證des有充足的空間
int dictGetRandomKeys(dict *d, dictEntry **des, int count) {
//哈希表下標
int j;
//用于判斷取到的節(jié)點數(shù)量是否足夠了
int stored = 0;
//當字典沒有那么多節(jié)點時,調(diào)整count
if (dictSize(d) < count) count = dictSize(d);
while(stored < count) {
for (j = 0; j < 2; j++) {
//隨機取一個索引
unsigned int i = random() & d->ht[j].sizemask;
int size = d->ht[j].size;
//遍歷該哈希表的所有索引
while(size--) {
//指向該索引鏈表的第一個節(jié)點
dictEntry *he = d->ht[j].table[i];
while (he) {
//該索引鏈表中有節(jié)點,取出來
*des = he;
des++;
//指向該鏈表的下一個節(jié)點
he = he->next;
stored++;
//取出的節(jié)點足夠了,函數(shù)返回
if (stored == count) return stored;
}
//如果該索引下沒有節(jié)點,或者索引鏈表下節(jié)點都取出來了,遍歷下一個索引
i = (i+1) & d->ht[j].sizemask;
}
//如果想訪問ht[1]哈希表,需要字典正在進行 rehash,不然不能訪問ht[1]
assert(dictIsRehashing(d) != 0);
}
}
return stored; /* Never reached. */
}