链表在redis中使用非常广泛,比如列表键的底层实现之一就是链表。
当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,redis就会使用链表最为列表键的底层实现。
链表节点数据结构
// 双端链表节点
typedef struct listNode {
struct listNode* prev; // 前置节点
struct listNode* next; // 后置节点
void* value; // 节点的值
} listNode;
image.png
双端链表数据结构
// 双端链表结构
typedef struct list {
listNode* head;
listNode* tail;
unsigned long len;
void* (*dup)(void* ptr) // 节点值复制函数
void* (*free)(void* ptr) // 节点值释放函数
void* (*match)(void* ptr) // 节点值对比函数
}
// 双端链表迭代器
typedef struct listIter {
listNode *next; // 当前迭代到的节点
int direction; // 迭代的方向
} listIter;
image.png
listCreate
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;
}
image.png
listAddNodeHead
头插法
list *listAddNodeHead(list *list, void *value)
{
listNode *node;
// 为节点分配内存
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
// 保存值指针
node->value = value;
// 添加节点到空链表
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++;
return list;
}
image.png
listAddNodeTail
listInsertNode
listDelNode
迭代器
/*
* AL_START_HEAD :从表头向表尾迭代
* AL_START_TAIL :从表尾想表头迭代
*/
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;
}
listNode *listNext(listIter *iter)
{
listNode *current = iter->next;
if (current != NULL) {
// 根据方向选择下一个节点
if (iter->direction == AL_START_HEAD)
// 保存下一个节点,防止当前节点被删除而造成指针丢失
iter->next = current->next;
else
// 保存下一个节点,防止当前节点被删除而造成指针丢失
iter->next = current->prev;
}
return current;
}
listSearchKey
listIndex
listRotate
void listRotate(list *list) {
listNode *tail = list->tail;
if (listLength(list) <= 1) return;
/* Detach current tail */
// 取出表尾节点
list->tail = tail->prev;
list->tail->next = NULL;
/* Move it as head */
// 插入到表头
list->head->prev = tail;
tail->prev = NULL;
tail->next = list->head;
list->head = tail;
}
特性总结
Redis的链表实现的特性可总结如下:
- 双端
获取某个节点的前置节点和后置节点的复杂度都是O(1)。 - 无环
以NULL为终点 - 带表头指针和表尾指针
获取表头和表尾节点的复杂度为O(1)。 - 带链表长度计数器
获取链表长度的复杂度为O(1)。 - 多态
链表节点使用void*
指针来保存节点值,并且可以通过list结构的三个属性节点值来设置类型特定函数,所以可用来保存不同类型的值。
网友评论