Redis源码解读(三)链表和链表结点的实现

149 阅读2分钟

链表结点和链表结构

每个链表节点使用一个adlist.h/listNode结构来表示:

/*
 * 链表节点
 */
typedef struct listNode {

    // 前驱节点
    struct listNode *prev;

    // 后继节点
    struct listNode *next;

    // 值
    void *value;

} listNode;

通过prev和next形成双向链表,通过链表来管理操作链表结点,也允许通过迭代器来访问链表

/*
 * 链表
 */
typedef struct list {

    // 表头指针
    listNode *head;

    // 表尾指针
    listNode *tail;

    // 节点数量
    unsigned long len;

    // 复制函数
    void *(*dup)(void *ptr);
    // 释放函数
    void (*free)(void *ptr);
    // 比对函数
    int (*match)(void *ptr, void *key);
} list;

链表迭代器

typedef struct listIter {

    // 下一节点
    listNode *next;

    // 迭代方向
    int direction;

} listIter;

Redis链表的特性

  • 双端:链表结点带有prev和next指针,获取某个结点的前置结点和后置结点的复杂度都是O(1)
  • 无环: 表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为终点。
  • 带链表长度计数器 程序获取链表中结点个数的复杂度为O(1)
  • 多态,可以保存不同类型的值

源码特点

一些比较简单的工具函数如listLength,listFirst,listLast等,使用宏定义

/*
 * 创建一个新列表
 *
 * 创建成功时返回列表,创建失败返回 NULL
 *
 * T = O(1)
 */
list *listCreate(void)
{
    struct list *list;

    // 为列表结构分配内存
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;

    // 初始化属性
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;

    return list;
}
/*
 * 释放整个列表(以及列表包含的节点)
 *
 * T = O(N),N 为列表的长度
 */
void listRelease(list *list)
{
    unsigned long len;
    listNode *current, *next;

    current = list->head;
    len = list->len;
    while(len--) {
        next = current->next;
        // 如果列表有自带的 free 方法,那么先对节点值调用它
        if (list->free) list->free(current->value);
        // 之后再释放节点
        zfree(current);
        current = next;
    }
    zfree(list);
}
/*
 * 复制整个列表,成功返回列表的副本,内存不足而失败时返回 NULL 。
 *
 * 无论复制是成功或失败,输入列表都不会被修改。
 *
 * T = O(N),N 为 orig 列表的长度
 */
list *listDup(list *orig)
{
    list *copy;
    listIter *iter;
    listNode *node;

    if ((copy = listCreate()) == NULL)
        return NULL;

    // 复制属性
    copy->dup = orig->dup;
    copy->free = orig->free;
    copy->match = orig->match;

    // 复制节点
    iter = listGetIterator(orig, AL_START_HEAD);
    while((node = listNext(iter)) != NULL) {

        // 复制节点值
        void *value;
        
        if (copy->dup) {
            value = copy->dup(node->value);
            if (value == NULL) {
                listRelease(copy);
                listReleaseIterator(iter);
                return NULL;
            }
        } else
            value = node->value;
        
        // 将新节点添加到新列表末尾
        if (listAddNodeTail(copy, value) == NULL) {
            listRelease(copy);
            listReleaseIterator(iter);
            return NULL;
        }
    }

    listReleaseIterator(iter);

    return copy;
}