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); }