Redis 设计与实现(第三章) -- 链表adlist

Posted qiezijiajia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 设计与实现(第三章) -- 链表adlist相关的知识,希望对你有一定的参考价值。

概述


 

1.链表介绍

2.链表API

链表介绍

链表在Redis中的应用非常广泛,比如列表键list的底层实现就是使用的链表。除了列表键外,Redis的发布与订阅、慢查询、监视器等功能也用到了链表,Redis服务器本身还使用了链表来保存客户端连接状态,以后使用链表来构建客户端输出缓冲区。

链表在Redis的数据结构如下:

typedef struct listNode {
    struct listNode *prev;  //前一个节点
    struct listNode *next;  //后一个节点,可以看出链表为双向链表
    void *value;  //节点值
} listNode;

其实可以通过多个listNode链接起来,就是一个双向链表,但是使用list结构来持有链表会更方便,如下:

typedef struct list {
    listNode *head;  //链表头节点
    listNode *tail;  //尾节点
    void *(*dup)(void *ptr);   //节点值复制函数
    void (*free)(void *ptr);  //节点值释放函数
    int (*match)(void *ptr, void *key);  //节点值比较函数
    unsigned long len; //链表包含的节点数量
} list;

Redis中链表的特点:

1.双向,有prev和next指针指向前/后一个节点;

2.无环,header的prev和tail的next都指向null;

3.带表头和表尾指针,快速获取表头/尾;

4.带链表长度值;

5.多态,通过void* 来保存节点值,可以通过函数为节点值设置类型特定函数,所以链表能够保存各种不同类型的值。

链表API

添加头部节点,和基本链表操作类似

list *listAddNodeHead(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    list->len++;
    return list;
}

search节点

listNode *listSearchKey(list *list, void *key)
{
    listIter *iter;
    listNode *node;

    iter = listGetIterator(list, AL_START_HEAD); //获取遍历器
    while((node = listNext(iter)) != NULL) {  //遍历节点
        if (list->match) { //match函数不为空,即设置了链表的值比较函数
            if (list->match(node->value, key)) {  //根据match函数来比较
                listReleaseIterator(iter);
                return node;
            }
        } else {  //没设置,直接比较
            if (key == node->value) {
                listReleaseIterator(iter);
                return node;
            }
        }
    }
    listReleaseIterator(iter);
    return NULL;
}

遍历器

typedef struct listIter {  //设置一个listIter遍历器
    listNode *next;
    int direction; //direction值为AL_START_HEAD(0)头部节点开始,AL_START_TAIL(1)尾部节点开始
} listIter;

获取迭代器的方法:

listIter *listGetIterator(list *list, int direction)
{
    listIter *iter;

    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
    if (direction == AL_START_HEAD)  //从头部节点开始
        iter->next = list->head; //设置next节点为head
    else
        iter->next = list->tail;
    iter->direction = direction;
    return iter;
}

然后通过next函数通过迭代器遍历:

/* Return the next element of an iterator.
 * Its valid to remove the currently returned element using
 * listDelNode(), but not to remove other elements.
 *
 * The function returns a pointer to the next element of the list,
 * or NULL if there are no more elements, so the classical usage patter
 * is:
 *使用方法
 * iter = listGetIterator(list,<direction>);
 * while ((node = listNext(iter)) != NULL) {
 *     doSomethingWith(listNodeValue(node));
 * }
 *
 * */
listNode *listNext(listIter *iter)
{
    listNode *current = iter->next;

    if (current != NULL) {
        if (iter->direction == AL_START_HEAD)
            iter->next = current->next;
        else
            iter->next = current->prev;
    }
    return current;
}

 

以上是关于Redis 设计与实现(第三章) -- 链表adlist的主要内容,如果未能解决你的问题,请参考以下文章

Redis设计与实现 : 链表

学习笔记-Redis设计与实现-链表

读书笔记-《Redis设计与实现》数据结构与对象

读书笔记-《Redis设计与实现》数据结构与对象

读书笔记-《Redis设计与实现》数据结构与对象

《Redis 的设计与实现》pdf分享下载