redis6.0.5之dict阅读笔记6-dict之元素删除和替代

redis6.0.5之dict阅读笔记6-dict之元素删除和替代
元素操作的四大,增删改查,我们已经看过了增和查,现在来看看删和改
******************************************************************
/* Search and remove an element. This is an helper function for
 * dictDelete() and dictUnlink(), please check the top comment
 * of those functions. */
 搜索并且移除一个元素,这函数是为函数dictDelete和dictUnlink做准备的。请参考这些函数的头部注释
static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {
    uint64_t h, idx;
    dictEntry *he, *prevHe;
    int table;

    if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL; //如果hash表总元素为空,直接返回空

    if (dictIsRehashing(d)) _dictRehashStep(d); //如果正在做rehashing,那么帮忙做一桶的迁移工作
    h = dictHashKey(d, key);获取key相关的hash值

    for (table = 0; table <= 1; table++) { //查找table0,table1两张hash表
        idx = h & d->ht[table].sizemask; //获取所在桶的索引
        he = d->ht[table].table[idx]; //获取桶的首元素
        prevHe = NULL;
        while(he) {
            if (key==he->key || 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 he;
            }
            prevHe = he; //没有找到相同额元素,将自身赋值给prevHe保存
            he = he->next; //查找下一个
        }
        if (!dictIsRehashing(d)) break; //没有做rehashing,那么值需要查找表0即可
    }
    return NULL; /* not found */没有找到元素,返回空
}
******************************************************************
/* Remove an element, returning DICT_OK on success or DICT_ERR if the
 * element was not found. */
移除一个元素,返回成功当移除成功时或者错误当元素没有找到时
int dictDelete(dict *ht, const void *key) {
    return dictGenericDelete(ht,key,0) ? DICT_OK : DICT_ERR;
}
******************************************************************
/* Remove an element from the table, but without actually releasing
 * the key, value and dictionary entry. The dictionary entry is returned
 * if the element was found (and unlinked from the table), and the user
 * should later call `dictFreeUnlinkedEntry()` with it in order to release it.
 * Otherwise if the key is not found, NULL is returned.
从字典中移除一个元素,但不真正释放键和值以及元素。如果找到了,那么这个字典中的元素就返回,
并且从表中移除,用户在使用完后应该调用dictFreeUnlinkedEntry释放它;如果没有找到,返回空
 * This function is useful when we want to remove something from the hash
 * table but want to use its value before actually deleting the entry.
 * Without this function the pattern would require two lookups:
当我们想从hash表中移除某个元素但是又想在删除这个元素之前使用这个元素的时候,
这个函数帮助我们避免了两次查询。下面是例子
 *  entry = dictFind(...);  //查询是一遍
 *  // Do something with entry
 *  dictDelete(dictionary,entry);  //删除是一遍,总的两遍
 *
 * Thanks to this function it is possible to avoid this, and use
 * instead:
由于使用这个函数,我们可以用如下的方式避免这个问题
 * entry = dictUnlink(dictionary,entry); //移除元素,返回移除值,只需一遍即可
 * // Do something with entry
 * dictFreeUnlinkedEntry(entry); // <- This does not need to lookup again.
 */
dictEntry *dictUnlink(dict *ht, const void *key) {
    return dictGenericDelete(ht,key,1); // 只从表中移除,不真正释放
}
****************************************************************************
/* You need to call this function to really free the entry after a call
 * to dictUnlink(). It's safe to call this function with 'he' = NULL. */
在使用完dictUnlink返回的元素后,我们需要调用这个函数 去真正的释放返回的元素。
即使dictUnlink返回的元素的空,这个函数也是安全的。
void dictFreeUnlinkedEntry(dict *d, dictEntry *he) {
    if (he == NULL) return; //为空的情况
    dictFreeKey(d, he);
    dictFreeVal(d, he);
    zfree(he);
}
****************************************************************************
/* Destroy an entire dictionary */ 删除整个字典
int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {
    unsigned long i;

    /* Free all the elements */
    for (i = 0; i < ht->size && ht->used > 0; i++) { //遍历所有的桶
        dictEntry *he, *nextHe;

        if (callback && (i & 65535) == 0) callback(d->privdata);
//目前不知道这句话的作用,从字面上看,是当传入了回调函数并且i是2的16次方的倍数时候(2^16 = 65536), 就调用这个回调函数

        if ((he = ht->table[i]) == NULL) continue; /如果是个空桶,继续下一个桶
        while(he) { 
            nextHe = he->next; //将下一个元素先取出来
            dictFreeKey(d, he); 
            dictFreeVal(d, he);
            zfree(he);
            ht->used--; //元素计数减1
            he = nextHe; //迭代下一个
        }
    }
    /* Free the table and the allocated cache structure */
    zfree(ht->table); //释放链表空间
    /* Re-initialize the table */
    _dictReset(ht);/重置hash表结构
    return DICT_OK; /* never fails */
}

****************************************************************************
/* Clear & Release the hash table */
清空字典
void dictRelease(dict *d)
{
    _dictClear(d,&d->ht[0],NULL); //清空表0
    _dictClear(d,&d->ht[1],NULL); //清空表1 
    zfree(d); //清空字典
}
****************************************************************************
/* Add or Overwrite:
 * Add an element, discarding the old value 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. */
添加元素或者重写元素:
添加一个元素,如果同样的键已经存在,丢弃原来的值。
如果是新增的键,那么返回1,如果已经存在同样的键,那么这个函数就是重置值,返回0
int dictReplace(dict *d, void *key, void *val)
{
    dictEntry *entry, *existing, auxentry;

    /* Try to add the element. If the key
     * does not exists dictAdd will succeed. */
     如果不存在同样的键,那么成功添加一个新键
    entry = dictAddRaw(d,key,&existing);
    if (entry) {
        dictSetVal(d, entry, val); //添加新键的值
        return 1;
    }

    /* 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 = *existing; //将原值赋给一个辅助变量
    dictSetVal(d, existing, val); //设置新值
    dictFreeVal(d, &auxentry);//释放原值
    return 0;
}
****************************************************************************
清空字典
void dictEmpty(dict *d, void(callback)(void*)) {
    _dictClear(d,&d->ht[0],callback);
    _dictClear(d,&d->ht[1],callback);
    d->rehashidx = -1;
    d->iterators = 0;
}
****************************************************************************
/* Resize the table to the minimal size that contains all the elements,
 * but with the invariant of a USED/BUCKETS ratio near to <= 1 */
将表的大小调整到可以容纳所有元素的最小值,但是USED(已保存元素个数)/BUCKETS(桶数)的固定比例还是小于等于1
int dictResize(dict *d)
{
    unsigned long minimal;
    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR; 
    //如果不允许调整大小或者正在做rehashing
    
    minimal = d->ht[0].used; //最小需要已保存元素的个数
    if (minimal < DICT_HT_INITIAL_SIZE)  //保证最小值不小于4
        minimal = DICT_HT_INITIAL_SIZE;
    return dictExpand(d, minimal); //进行收缩
}
****************************************************************************

 

posted on 2020-08-18 19:31  子虚乌有  阅读(336)  评论(0)    收藏  举报