1 dictReplace
1.1 方法说明
添加一个元素,如果该键已经存在,则丢弃旧的。
1.2 方法源码
/* Add an element, discarding the old if the key already exists.
* Return 1 if the key was added from scratch, 0 if there was already an
* element with such key and dictReplace() just performed a value update
* operation. */
int dictReplace(dict *d, void *key, void *val)
{
dictEntry *entry, auxentry;
/* Try to add the element. If the key
* does not exists dictAdd will suceed. */
if (dictAdd(d, key, val) == DICT_OK)
return 1;
/* It already exists, get the entry */
entry = dictFind(d, key);
/* Set the new value and free the old one. Note that it is important
* to do that in this order, as the value may just be exactly the same
* as the previous one. In this context, think to reference counting,
* you want to increment (set), and then decrement (free), and not the
* reverse. */
auxentry = *entry;
dictSetVal(d, entry, val);
dictFreeVal(d, &auxentry);
return 0;
}
1.3 代码理解
- 先尝试去增加一个节点,如果增加成功则返回 1 。
- 如果增加失败表示这个键已存在,则根据键寻找这个节点。
- 先设置新值,在释放旧值
2 dictGenericDelete
2.1 方法说明
字典删除节点通用方法
2.2 方法源码
/* Search and remove an element */
static int dictGenericDelete(dict *d, const void *key, int nofree)
{
unsigned int h, idx;
dictEntry *he, *prevHe;
int table;
// 判断哈希表长度是否为 0
if (d->ht[0].size == 0) return DICT_ERR; /* d->ht[0].table is NULL */
if (dictIsRehashing(d)) _dictRehashStep(d);
// 计算 hash 值
h = dictHashKey(d, key);
// 遍历两个 hash 表
for (table = 0; table <= 1; table++) {
// 计算索引值
idx = h & d->ht[table].sizemask;
// 获取节点
he = d->ht[table].table[idx];
prevHe = NULL;
// 遍历相同索引位置所有节点
while(he) {
if (dictCompareKeys(d, key, he->key)) {
/* Unlink the element from the list */
if (prevHe)
prevHe->next = he->next;
else
d->ht[table].table[idx] = he->next;
if (!nofree) {
dictFreeKey(d, he);
dictFreeVal(d, he);
}
// 释放节点,并且节点使用数量减一
zfree(he);
d->ht[table].used--;
return DICT_OK;
}
prevHe = he;
he = he->next;
}
if (!dictIsRehashing(d)) break;
}
return DICT_ERR; /* not found */
}
2.3 相关代码
2.3.1 dictDelete
int dictDelete(dict *ht, const void *key) {
return dictGenericDelete(ht,key,0);
}
2.3.2 dictDeleteNoFree
int dictDeleteNoFree(dict *ht, const void *key) {
return dictGenericDelete(ht,key,1);
}
2.4 代码理解
- 判断哈希表长度是否为 0,为 0 的话直接返回错误。
- 判断当前是否正在进行 rehash 中,如果是的话则进行一步 rehash 操作。
- 计算要删除节点的 hash 值
- 遍历两个哈希表,都要进行相应地删除操作。
- 根据 hash 值和掩码计算出键的索引值位置。
- 遍历索引值位置所有的节点,比较键是否相同,找到该节点。
- 找到节点之后释放节点,并且让哈希表的数量减 1 。
- 如果正在 rehash 中,则不删除第二个哈希表中的节点。
3 dictFind
3.1 方法说明
寻找一个哈希节点,如果找到就返回哈希节点,没有返回 NULL。
3.2 方法源码
dictEntry *dictFind(dict *d, const void *key)
{
dictEntry *he;
unsigned int h, idx, table;
if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */
if (dictIsRehashing(d)) _dictRehashStep(d);
h = dictHashKey(d, key);
for (table = 0; table <= 1; table++) {
idx = h & d->ht[table].sizemask;
he = d->ht[table].table[idx];
while(he) {
if (dictCompareKeys(d, key, he->key))
return he;
he = he->next;
}
if (!dictIsRehashing(d)) return NULL;
}
return NULL;
}
3.3 方法理解
- 判断哈希表长度是否为 0,为 0 的话直接返回错误。
- 判断当前是否正在进行 rehash 中,如果是的话则进行一步 rehash 操作。
- 计算要删除节点的 hash 值
- 遍历两个哈希表。
- 根据 hash 值和掩码计算出键的索引值位置。
- 遍历索引值位置所有的节点,比较键是否相同,如果找到则返回该节点。
- 如果正在 rehash 中,遍历完第1个哈希表之后没找到则返回 NULL。
4 学习总结
- 本节主要学习了字典中的 dictReplace、dictGenericDelete、dictFind 三个方法。
- 可以看到的字典中大部分的代码都会判断当前是否进行 rehash 动作。
- 如果正在进行 rehash 动作的话,会进行一步的 rehash 动作。
- 如果正在进行 rehash 动作的话,不会删除第二个哈希表的节点。
- 可以发现大部分的方法都是一样的流程,计算哈希值,计算索引值,遍历索引节点。