Redis 数据结构 - 链表
链表 - List 的底层实现
链表提供了高效的节点重排能力,可以通过顺序访问的方式访问节点,并且支持增加删除节点调整长度。
由于 C 语言原生并不支持链表,redis 的链表是自己实现的。
List 的底层实现就是一个双向链表,支持从链表的两端进行push
和pop
操作,时间复杂度是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 的链表实现具有一下特性:
- 双端
- 链表自带头指针 prev 和尾指针 next,获取某个节点的前置节点和后置节点的时间复杂度都是 O(1)。
- 无环
- 表头节点的 prev 指针和表尾节点 next 指针都指向 null,对链表的访问以 null 为终点。
- 带表头指针和表尾指针
- 通过 list 结构的 head 指针和 tail 指针,程序获取链表的头节点和尾节点的时间复杂度为 O(1)。
- 多态
- 链表节点使用 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 结构表示的链表。