Redis 数据结构 - 链表

链表 - List 的底层实现

链表提供了高效的节点重排能力,可以通过顺序访问的方式访问节点,并且支持增加删除节点调整长度。

由于 C 语言原生并不支持链表,redis 的链表是自己实现的。

List 的底层实现就是一个双向链表,支持从链表的两端进行pushpop操作,时间复杂度是O(1)。同时支持在任意位置进行存取操作,但是需要进行整体的遍历,从而时间复杂度为O(n)。List 的应用场景很多,通常被用作队列使用。同时,微博的关注列表,粉丝列表,消息队列等功能都可以用 redis 的 list 结构来实现。可以利用 lrange 命令,做基于 redis 的分页功能。

除了 List 外,发布订阅、慢查询、监视器等功能也都用到了链表,Redis 本身服务器还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区(output buffer)。

链表和链表节点的实现

/* Node, List, and Iterator are the only data structures used currently. */
/*
 * 双端链表节点
 */
typedef struct listNode {
    // 前置节点
    struct listNode *prev;
    // 后置节点
    struct listNode *next;
    // 节点的值
    void *value;
} listNode;

每个节点可以知晓自己的前置节点和后置节点,多个 listNode 就可以组成一个双向链表,但是 redis 额外提供了一个 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;

所以一个链表结构由 一个 list 结构和若干个 listNode 结构组成:

由结构可知,redis 的链表实现具有一下特性:

  1. 双端
    • 链表自带头指针 prev 和尾指针 next,获取某个节点的前置节点和后置节点的时间复杂度都是 O(1)。
  2. 无环
    • 表头节点的 prev 指针和表尾节点 next 指针都指向 null,对链表的访问以 null 为终点。
  3. 带表头指针和表尾指针
    • 通过 list 结构的 head 指针和 tail 指针,程序获取链表的头节点和尾节点的时间复杂度为 O(1)。
  4. 多态
    • 链表节点使用 void* 指针来保存节点值,并且可以通过 list 结构的 dup、free、match 三个属性为节点值实现特定的类型函数,所以链表可以用来保存不同的值。list 本身可以视为一个抽象。

链表和链表节点的 API

创建

/* Create a new list. The created list can be freed with
 * listRelease(), but private value of every node need to be freed
 * by the user before to call listRelease(), or by setting a free method using
 * listSetFreeMethod.
 *
 * On error, NULL is returned. Otherwise the pointer to the new list. */
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;
}

释放

/* Remove all the elements from the list without destroying the list itself. */
void listEmpty(list *list)
{
    unsigned long len;
    listNode *current, *next;
		// 从头指针开始遍历
    current = list->head;
    len = list->len;
    while(len--) {
        next = current->next;
      	// 如果存在释放函数,那么执行它,释放节点值
        if (list->free) list->free(current->value);
        // 释放节点自身结构
      	zfree(current);
        current = next;
    }
  	// 头、尾节点置为空
    list->head = list->tail = NULL;
    list->len = 0;
}

/* Free the whole list.
 *
 * This function can't fail. */
void listRelease(list *list)
{
  	// 删除所有链表元素
    listEmpty(list);
  	// 释放链表结构
    zfree(list);
}

添加一个节点

添加到头位置
/* Add a new node to the list, to head, containing the specified 'value'
 * pointer as value.
 *
 * On error, NULL is returned and no operation is performed (i.e. the
 * list remains unaltered).
 * On success the 'list' pointer you pass to the function is returned. */
list *listAddNodeHead(list *list, void *value)
{
    listNode *node;
		// 分配内存错误返回 null
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
  	// 新的节点记录节点值
    node->value = value;
  	// 
    listLinkNodeHead(list, node);
    return list;
}

/*
 * Add a node that has already been allocated to the head of list
 */
void listLinkNodeHead(list* list, listNode *node) {
    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++;
}
添加到尾位置
/* Add a new node to the list, to tail, containing the specified 'value'
 * pointer as value.
 *
 * On error, NULL is returned and no operation is performed (i.e. the
 * list remains unaltered).
 * On success the 'list' pointer you pass to the function is returned. */
list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    listLinkNodeTail(list, node);
    return list;
}

/*
 * Add a node that has already been allocated to the tail of list
 */
void listLinkNodeTail(list *list, listNode *node) {
    if (list->len == 0) {
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }
    list->len++;
}

指定节点(前\后)

/*
 * 创建一个包含值 value 的新节点,并将它插入到 old_node 的之前或之后
 *
 * 如果 after 为 0 ,将新节点插入到 old_node 之前。
 * 如果 after 为 1 ,将新节点插入到 old_node 之后。
 *
 * T = O(1)
 */
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    listNode *node;

    // 创建新节点
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 保存值
    node->value = value;

    // 将新节点添加到给定节点之后
    if (after) {
        node->prev = old_node;
        node->next = old_node->next;
        // 给定节点是原表尾节点
        if (list->tail == old_node) {
            list->tail = node;
        }
    // 将新节点添加到给定节点之前
    } else {
        node->next = old_node;
        node->prev = old_node->prev;
        // 给定节点是原表头节点
        if (list->head == old_node) {
            list->head = node;
        }
    }

    // 更新新节点的前置指针
    if (node->prev != NULL) {
        node->prev->next = node;
    }
    // 更新新节点的后置指针
    if (node->next != NULL) {
        node->next->prev = node;
    }

    // 更新链表节点数
    list->len++;

    return list;
}

释放一个节点

/* Remove the specified node from the specified list.
 * The node is freed. If free callback is provided the value is freed as well.
 *
 * This function can't fail. */
void listDelNode(list *list, listNode *node)
{
  	// 修改链表
    listUnlinkNode(list, node);
  	// 如果提供了值释放函数,那么释放节点值
    if (list->free) list->free(node->value);
  	// 释放节点结构
    zfree(node);
}

/*
 * Remove the specified node from the list without freeing it.
 */
void listUnlinkNode(list *list, listNode *node) {
  	// 调整前置节点
    if (node->prev)
      	// 非头节点
        node->prev->next = node->next;
    else
      	// 头节点
        list->head = node->next;
  	// 调整后置节点 
    if (node->next)
      	// 非尾节点
        node->next->prev = node->prev;
    else
      	// 尾节点
        list->tail = node->prev;
		// 置空指针
    node->next = NULL;
    node->prev = NULL;
		// 链表长度减少
    list->len--;
}

迭代

/* Returns a list iterator 'iter'. After the initialization every
 * call to listNext() will return the next element of the list.
 *
 * This function can't fail. */
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;
    else
        iter->next = list->tail;
    iter->direction = direction;
    return iter;
}

/* Release the iterator memory */
void listReleaseIterator(listIter *iter) {
    zfree(iter);
}

/* Create an iterator in the list private iterator structure */
void listRewind(list *list, listIter *li) {
    li->next = list->head;
    li->direction = AL_START_HEAD;
}

void listRewindTail(list *list, listIter *li) {
    li->next = list->tail;
    li->direction = AL_START_TAIL;
}

查找

/* Search the list for a node matching a given key.
 * The match is performed using the 'match' method
 * set with listSetMatchMethod(). If no 'match' method
 * is set, the 'value' pointer of every node is directly
 * compared with the 'key' pointer.
 *
 * On success the first matching node pointer is returned
 * (search starts from head). If no matching node exists
 * NULL is returned. */
listNode *listSearchKey(list *list, void *key)
{
    listIter iter;
    listNode *node;

    listRewind(list, &iter);
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {
            if (list->match(node->value, key)) {
                return node;
            }
        } else {
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}

查找链表 list 中值和 key 匹配的节点。对比操作由链表的 match 函数负责进行,如果没有设置 match 函数,那么直接通过对比值的指针来决定是否匹配。如果匹配成功,那么第一个匹配的节点会被返回。如果没有匹配任何节点,那么返回 NULL 。时间复杂度 T = O(N)。

/* Return the element at the specified zero-based index
 * where 0 is the head, 1 is the element next to head
 * and so on. Negative integers are used in order to count
 * from the tail, -1 is the last element, -2 the penultimate
 * and so on. If the index is out of range NULL is returned. */
listNode *listIndex(list *list, long index) {
    listNode *n;
		
  	// 如果索引为负数,从表尾开始查找
    if (index < 0) {
        index = (-index)-1;
        n = list->tail;
        while(index-- && n) n = n->prev;
    } else {
      	// 如果索引为正数,从表头开始查找
        n = list->head;
        while(index-- && n) n = n->next;
    }
    return n;
}

拼接

/* Add all the elements of the list 'o' at the end of the
 * list 'l'. The list 'other' remains empty but otherwise valid. */
// 默认将 list o 的所有元素追加到 list l 的尾
void listJoin(list *l, list *o) {
    if (o->len == 0) return;

    o->head->prev = l->tail;

    if (l->tail)
        l->tail->next = o->head;
    else
        l->head = o->head;

    l->tail = o->tail;
    l->len += o->len;

    /* Setup other as an empty list. */
    o->head = o->tail = NULL;
    o->len = 0;
}

总结

redis 的链表,是一个双端、无环、支持多态的用一个 list 结构表示的链表。

posted @ 2023-07-12 11:53  萝卜不会抛异常  阅读(193)  评论(0编辑  收藏  举报