美文网首页
Redis 源码--字典P3 查找,删除和修改。

Redis 源码--字典P3 查找,删除和修改。

作者: namelessEcho | 来源:发表于2017-10-01 14:21 被阅读0次

Redis的查找dictAdd通过一个底层的dictAddRaw方法来实现。

int dictAdd(dict *d, void *key, void *val)
{
    // 尝试添加键到字典,并返回包含了这个键的新哈希节点
    // T = O(N)
    dictEntry *entry = dictAddRaw(d,key);

    // 键已存在,添加失败
    if (!entry) return DICT_ERR;

    // 键不存在,设置节点的值
    // T = O(1)
    dictSetVal(d, entry, val);

    // 添加成功
    return DICT_OK;
}

前面提到,如果在rehash过程中且不存在安全迭代器,插入和更新方法都会调用单步rehash 所以dictAddRaw()调用了它。dictAddRaw()方法并不直接将key插入到table中,而是返回需要的Entry给add方法,如果已经存在这个key,那么他会返回null,如果不存在这个key,会返回入口,为了性能,对于桶内新加入的key,我们总是把它插入到头部。

dictEntry *dictAddRaw(dict *d, void *key)
{
    int index;
    dictEntry *entry;
    dictht *ht;

    // 如果条件允许的话,进行单步 rehash
    // T = O(1)
    if (dictIsRehashing(d)) _dictRehashStep(d);

    /* Get the index of the new element, or -1 if
     * the element already exists. */
    // 计算键在哈希表中的索引值
    // 如果值为 -1 ,那么表示键已经存在
    // T = O(N)
    if ((index = _dictKeyIndex(d, key)) == -1)
        return NULL;

    // T = O(1)
    /* Allocate the memory and store the new entry */
    // 如果字典正在 rehash ,那么将新键添加到 1 号哈希表
    // 否则,将新键添加到 0 号哈希表
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
    // 为新节点分配空间
    entry = zmalloc(sizeof(*entry));
    // 将新节点插入到链表表头
    entry->next = ht->table[index];
    ht->table[index] = entry;
    // 更新哈希表已使用节点数量
    ht->used++;

    /* Set the hash entry fields. */
    // 设置新节点的键
    // T = O(1)
    dictSetKey(d, entry, key);

    return entry;
}

回到add方法,它会对raw方法返回的entry进行判断来觉得是否进行add。
综上,add方法是不可以插入已经存在的key进行操作的。
如果需要改变已经存在的key的VAL,我们需要使用replace方法。
类似的,replace方法也有一个raw方法,使用情况和add类似。

dictEntry *dictReplaceRaw(dict *d, void *key) {
    
    // 使用 key 在字典中查找节点
    // T = O(1)
    dictEntry *entry = dictFind(d,key);

    // 如果节点找到了直接返回节点,否则添加并返回一个新节点
    // T = O(N)
    return entry ? entry : dictAddRaw(d,key);
}

最后是删除操作,由于删除操作要涉及到内存的清除工作,需要手动释放内存,所以写起来比其他两个操作要复杂一些。删除方法的通用方法是dictGenericDelete,其他方法大多调用了他。如果此时是rehash过程,那么明显的,需要查找TABLE[0]和TABLE[1];否则只需要查找TABLE[0];

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;
        // 遍历链表上的所有节点
        // T = O(1)
        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;
        }

        // 如果执行到这里,说明在 0 号哈希表中找不到给定键
        // 那么根据字典是否正在进行 rehash ,决定要不要查找 1 号哈希表
        if (!dictIsRehashing(d)) break;
    }

    // 没找到
    return DICT_ERR; /* not found */
}

相关文章

  • Redis 源码--字典P3 查找,删除和修改。

    Redis的查找dictAdd通过一个底层的dictAddRaw方法来实现。 前面提到,如果在rehash过程中且...

  • Redis 源码--字典P3 初始化和Rehash。

    一个字典所要做的最重要的工作就是保证快速的查找,删除和修改值。 首先可以来看一下Redis的初始化过程。 Redi...

  • python基础4_字典以及字符串操作

    字典两大特点:无序,键唯一 字典的创建 字典的转换 修改 查找 删除 其他操作 字符串操作 String的内置方法...

  • 字典

    创建字典 访问字典中的值 修改、添加字典 修改字典中的值 在末尾增添新的键/值 删除字典元素 删除字典 清空字典 ...

  • 2021-02-19 python 4-5章学习

    字典字典修改和新增操作删除 del 字典名 [键]和pop(item)和popitem()方法clear方法对字典...

  • Redis 源码分析(四) :intset

    Redis 源码分析(四) :intset一、什么是intset二、数据结构定义创建集合新增元素查找元素删除元素升...

  • 2. 字典和集合

    字典和集合相比于列表和元组,字典和集合的性能更优:主要体现在查找、增加和删除操作; 1. 字典和集合基础 字典是一...

  • 数据结构和算法二(ArrayList和LinkedList源码解

    ArrayList源码解析 ArrayList 是基于数组的方式来实现数据的增加、删除、修改、查找1、内部维护了两...

  • Swift 02 字典

    字典 创建字典和创建数组一样创建同时就要制定key和value值的类型 字典的修改和增加 字典的删除 字典的遍历

  • python 字典

    字典 ❤ 创建使用花括号{};修改和添加元素的方法:字典名[字典的键]=值;删除元素:del 字典名[字典的键] ...

网友评论

      本文标题:Redis 源码--字典P3 查找,删除和修改。

      本文链接:https://www.haomeiwen.com/subject/zytaextx.html