Redis设计与实现之链表
链表简介
链表提供了 高效的节点重排能力, 以及 顺序性的节点访问 方式, 并且可以通过增删节点来灵活地调整链表的长度。作为一种常用数据结构, 链表内置在很多高级的编程语言里面, 因为 Redis 使用的 C 语言并没有内置这种数据结构, 所以 Redis 构建了自己的链表实现。
链表及链表节点的实现
源码定义
源码位置 : adlist.h
/*
* list的节点定义
*/
typedef struct listNode {
struct listNode *prev; //前驱指针
struct listNode *next; //后继指针
void *value; //节点值
} 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;
head
: 表头指针tail
: 表尾指针len
: 链表长度计数器dup
: 函数用于复制链表节点所保存的值free
: 函数用于释放链表节点所保存的值match
: 函数则用于对比链表节点所保存的值和另一个输入值是否相等
迭代器定义
代码位置 : adlist.h
/**
* 迭代器
*/
typedef struct listIter {
listNode *next; //当前节点
int direction; //迭代方向
} listIter;
#define AL_START_HEAD 0 //从表头向表尾进行迭代
#define AL_START_TAIL 1 //从表尾到表头进行迭代
redis链表特性
- 双端 : 每个节点都有prev与next指针, 获取某个节点的前置节点和后置节点时间复杂度均为O(1)
- 无环 : 表头节点的prev与表尾节点的next均指向NULL,链表的访问以 NULL结束
- 带表头指针与表尾指针 : 即list结构中的head指针与tail指针,获取表头节点与表尾节点的时间复杂度为O(1)
- 带链表长度计数器 : 即list结构中的len属性,湖区链表长度的时间复杂度O(1)
- 多态 : 链表节点使用 void * 来保存节点值,并且可以通过list结构的dup、free、match三个属性尾节点值设置类型特定函数,所以链表可以用于保存各种类型不同的值
链表和链表节点的API
函数 | 作用 | 时间复杂度 |
---|---|---|
listSetDupMethod | 将给定的函数设置为链表的节点值复制函数。 | O(1) |
listGetDupMethod | 返回链表当前正在使用的节点值复制函数。 | 复制函数可以通过链表的 dup 属性直接获得, O(1) |
listSetFreeMethod | 将给定的函数设置为链表的节点值释放函数。 | O(1) |
listGetFree | 返回链表当前正在使用的节点值释放函数。 | 释放函数可以通过链表的 free 属性直接获得, O(1) |
listSetMatchMethod | 将给定的函数设置为链表的节点值对比函数。 | O(1) |
listGetMatchMethod | 返回链表当前正在使用的节点值对比函数。 | 对比函数可以通过链表的 match 属性直接获得, O(1) |
listLength | 返回链表的长度(包含了多少个节点)。 | 链表长度可以通过链表的 len 属性直接获得, O(1) |
listFirst | 返回链表的表头节点。 | 表头节点可以通过链表的 head 属性直接获得, O(1) |
listLast | 返回链表的表尾节点。 | 表尾节点可以通过链表的 tail 属性直接获得, O(1) |
listPrevNode | 返回给定节点的前置节点。 | 前置节点可以通过节点的 prev 属性直接获得, O(1) |
listNextNode | 返回给定节点的后置节点。 | 后置节点可以通过节点的 next 属性直接获得, O(1) |
listNodeValue | 返回给定节点目前正在保存的值。 | 节点值可以通过节点的 value 属性直接获得, O(1) |
listCreate | 创建一个不包含任何节点的新链表。 | O(1) |
listAddNodeHead | 将一个包含给定值的新节点添加到给定链表的表头。 | O(1) |
listAddNodeTail | 将一个包含给定值的新节点添加到给定链表的表尾。 | O(1) |
listInsertNode | 将一个包含给定值的新节点添加到给定节点的之前或者之后。 | O(1) |
listSearchKey | 查找并返回链表中包含给定值的节点。 | O(N) , N 为链表长度 |
listIndex | 返回链表在给定索引上的节点。 | O(N) , N 为链表长度 |
listDelNode | 从链表中删除给定节点。 | O(1) |
listRotate | 将链表的表尾节点弹出,然后将被弹出的节点插入到链表的表头, 成为新的表头节点。 | O(1) |
listDup | 复制一个给定链表的副本。 | O(N) , N 为链表长度 |
listRelease | 释放给定链表,以及链表中的所有节点。 | O(N) , N 为链表长度 |
listEmpty | 移除链表所有的节点,但是不销毁链表 | O(N) , N 为链表长度 |
listGetIterator | 获取list的迭代器,初始化后,每次调用listNext(),返回链表的下一个元素 | O(1) |
listNext | 获取链表的下一个元素, 入参迭代器由listGetIterator获得 | O(1) |
listReleaseIterator | 释放迭代器的内存 | O(1) |
listRewind | 从私有的迭代器中创建一个从头至尾顺序的迭代器 | O(1) |
listRewindTail | 从私有的迭代器中创建一个从尾至头顺序的迭代器 | O(1) |
listJoin | 将一个列表o追加到另一个列表l的尾部,同时清空列表o | O(N),N为列表o的长度 |
参考资料
参考内容
书籍
: 《Redis 设计与实现》
实体书
: 可在京东搜索购买
注意事项
《Redis 设计与实现》 本书讲解基于redis 3.x 版本, 本人整理基于redis 5.0.6,期间跨了两个大版本, 部分功能具体实现已发生变化, 如发现整理内容与 《Redis 设计与实现》 讲解不一致, 可加 QQ : 2215508028 沟通,或在相关章节评论留言,会自动同步至issue
整理内容时参考的redis源码
版本
: 5.0.6
官方git
: redis官方GIT (commit :bb78454b0fef0dc5903328d037ac2520108e0044 (5.0.6版本,redis官方的commit))