redis-链表

1、redis源码使用C语言,C中并没有实现链表这种数据结构,所以redis构建了自己的链表实现
2、除了链表键之外,发布订阅、慢查询、监视器等功能也用到了链表,redis服务器本身还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区
3、链表节点结构
typedef struct listNode {

    // 前置节点
    struct listNode *prev;

    // 后置节点
    struct listNode *next;

    // 节点的值
    void *value;

} listNode;
由节点结构可知,这是一个双向链表。为了让操作更方便,redis还定义了list结构,list结构为链表提供了表头指针head、表尾指针tail、链表长度计数器len
/*
 * 双端链表结构
 */
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;
4、redis链表实现特性
1)双端
链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1)
2)无环
表头节点的prev指针和表尾节点next指针都指向NULL,对链表的访问以NULL为终点
3)带表头指针和表尾指针
程序获得链表的表头节点和表尾节点的复杂度为O(1)
4)带链表长度计数器
使用len字段来对链表持有的节点的个数进行计数,程序获取链表中节点数量的复杂度为O(1)
5)多态
链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值
5、链表命令
1)lpush命令
lpush命令将一个或多个元素插入到列表的头部,如果指定的key不存在,则先创建一个空列表,如果指定的key不是一个list类型,则返回一个错误。
2)lrange命令
lrange命令来查看列表中指定范围的元素
3)lpushx命令
lpushx命令也用于在列表头部插入元素。与lpush不同的是,当指定key不存在时,该命令并不会创建一个空列表,二是不进行任何操作,直接返回
4)rpush命令
使用rpush命令从列表的尾部插入一个或多个元素
5)rpushx命令
rpushx命令从列表尾部插入元素。与rpush不同的是,当指定列表不存在时,该命令并不会创建一个空列表,而是直接返回。
6)lpop命令
lpop命令移除并返回list的表头元素。如果列表为空,则返回nil。
7)rpop命令
rpop命令移除并返回列表的尾部元素,如果列表为空,则返回nil。
8)lrem命令
lrem命令从列表中移除前count次出现的值为value的元素,返回被移除的元素个数。
9)ltrim命令
ltrim命令用于剪切指定列表,并保留下标范围为[start, stop]的元素。其中,start、stop均是从0开始计数的,也支持用负数来表示与列表尾部的偏移量。
以上只列举了部分链表的常用命令
6、链表创建、释放源码
/*
 * 双端链表结构
 */
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;
/* Free the whole list.
 *
 * This function can't fail. */
/*
 * 释放整个链表,以及链表中所有节点
 *
 * T = O(N)
 */
void listRelease(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;
    }

    // 释放链表结构
    zfree(list);
}

猜你喜欢

转载自blog.csdn.net/weixin_39138071/article/details/80225479