链表是很常用的数据结构, 大多数高级语言都有内置的实现. 因为C并没有提供链表的实现, 所以Redis就自已实现了链表. 如果有上过数据结构与算法或者看过其他语言对链表的实现, 那么这一部份理解起来就超级easy. 可以参考 java 的 LinkedList 的实现.
redis 关于链表的实现主要在 adlist.h
和 adlist.c
两个文件
1 adlist.h 源码解析
1.1 链表结构体 list
//链表结构体
typedef struct list {
//链表首节点
listNode *head;
//链表尾节点
listNode *tail;
//结构体的函数相当于多态, 由具体的对象提供对应的函数实现
//链表复制指定节点的value的值, 值如果是结构体, 则需要对应的函数来拷贝
void *(*dup)(void *ptr);
//链表释放指定节点的value的内存的函数, 值如果是结构体, 则需要对应的函数释放内存
void (*free)(void *ptr);
//值的比较函数, 相当于 java 的equals, 用于比较结构体是否相等
int (*match)(void *ptr, void *key);
//链表长度
unsigned long len;
} list;
- 链表结构体中主要有首节点指针
head
, 尾节点指针tail
, 三个函数指针和链表长度len
, 首尾节点主要是为了方便向后和向前遍历, 链表长度主要是为了获取节点的数量的时间复杂度为O(1). - 链表结构体中还有三个函数指针, 主要是用于对
listNode
中的value
进行复制, 释放内存和比较是否相等. 这三个函数指针有点像多态, 根据value是不同的值, 从而使用不同实现的函数. - 为什么节点需要三个函数指针呢 ? 当
value
指针是结构体时, 就不能直接判断, 复制和释放内存, 需要根据具体的结构体来做定制化.
1.2 链表节点的结构体 listNode
//链表节点的结构体
typedef struct listNode {
//前一个节点的引用
struct listNode *prev;
//下一个节点的引用
struct listNode *next;
//当前节点的值, 相当于 Object 类型
void *value;
} listNode;
- value是Void *指针, 相当于java的Object, 可以存任何类型的值或结构
- 链表节点通过prev指针指向上一个节点, 通过next指针指向下一个节点, 从而将所有节点连在一起, 如下图:
1.3 链表迭代器结构体 listIter
//链表迭代器
typedef struct listIter {
//当前遍历节点
listNode *next;
//迭代方向, 可以向前或者向后
int direction;
} listIter;
//前面开始遍历
#define AL_START_HEAD 0
//后面开始遍历
#define AL_START_TAIL 1
-
next
可以理解为当前遍历的节点, 也可以理解为下一个节点, 因为调用listNext()
方法就是获取next的值 -
direction
表示迭代方向, 主要两个值,AL_START_HEAD
表示从head
节点向后遍历,AL_START_TAIL
表示从tail
节点向前遍历
1.4 链表头文件相关的函数声明
/* Functions implemented as macros */
//获取链表长度
#define listLength(l) ((l)->len)
//获取链表第一个节点
#define listFirst(l) ((l)->head)
//获取链表最后一个节点
#define listLast(l) ((l)->tail)
//获取链表前一个节点
#define listPrevNode(n) ((n)->prev)
//获取链表后一个节点
#define listNextNode(n) ((n)->next)
//获取链表节点的值
#define listNodeValue(n) ((n)->value)
//设置链表的复制函数
#define listSetDupMethod(l,m) ((l)->dup = (m))
//设置链表结构体的释放函数
#define listSetFreeMethod(l,m) ((l)->free = (m))
//设置链表结构体的匹配函数
#define listSetMatchMethod(l,m) ((l)->match = (m))
//获取链表的复制函数
#define listGetDupMethod(l) ((l)->dup)
//获取链表的释放函数
#define listGetFreeMethod(l) ((l)->free)
//获取链表的匹配函数
#define listGetMatchMethod(l) ((l)->match)
/* Prototypes */
//链表创建函数
list *listCreate(void);
//链表释放
void listRelease(list *list);
//清空链表
void listEmpty(list *list);
//添加节点到头部
list *listAddNodeHead(list *list, void *value);
//添加节点到尾部
list *listAddNodeTail(list *list, void *value);
//根据节点指定位置插入新节点
list *listInsertNode(list *list, listNode *old_node, void *value, int after);
//删除指定节点
void listDelNode(list *list, listNode *node);
//获取节点迭代器
listIter *listGetIterator(list *list, int direction);
//获取迭代器下一个节点
listNode *listNext(listIter *iter);
//释放迭代器
void listReleaseIterator(listIter *iter);
//复制链表
list *listDup(list *orig);
//根据 key 值查找节点
listNode *listSearchKey(list *list, void *key);
//根据索引获取节点
listNode *listIndex(list *list, long index);
//设置迭代器, 指定链表从head开始遍历
void listRewind(list *list, listIter *li);
//设置迭代器, 指定链表从tail开始遍历
void listRewindTail(list *list, listIter *li);
//将链表尾节点变头节点
void listRotateTailToHead(list *list);
//将链表头节点变尾节点
void listRotateHeadToTail(list *list);
//将链表o拼接到链表l后面
void listJoin(list *l, list *o);
2 adlist.c 源码解析
2.1 创建空链表函数 listCreate
//创建空链表
list *listCreate(void)
{
//声明链表
struct list *list;
//分配链表内存
if ((list = zmalloc(sizeof(*list))) == NULL)
return NULL;
//初始化首节点与尾节点都为空
list->head = list->tail = NULL;
//设置长度为 0
list->len = 0;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list;
}
- 分配链表内存, 然后将所有指针初始化为 NULL, 链表长度初始为 0
2.2 清空链表节点函数 listEmpty
//清空链表元素, 不回收链表对象的内存
void listEmpty(list *list)
{
//链表当前长度
unsigned long len;
//current 当前遍历指针
//next 下一个节点的指针
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;
}
//首节点和尾节点置 NULL
list->head = list->tail = NULL;
//长度设置为 0
list->len = 0;
}
- 根据链表长度, 遍历链表对节点进行释放内存直到链表为空
- 这个函数相当于删除所有节点
2.3 释放链表内存函数 listRelease
//释放链表内存
void listRelease(list *list)
{
//清空链表
listEmpty(list);
//释放链表内存
zfree(list);
}
- 先清空节点, 然后释放链表内存
2.4 添加节点到链表头 listAddNodeHead
//添加节点到头部
list *listAddNodeHead(list *list, void *value)
{
//节点指针
listNode *node;
//分配节点内存, 分配不了则直接返回 NULL
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
//设置节点的值
node->value = value;
//如果链表长度为0 , 则表示链表为空, 直接指定首节点和尾节点
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;
}
//链表长度加 1
list->len++;
//返回链表
return list;
}
2.5 添加节点到链表尾
//添加节点到链表尾
list *listAddNodeTail(list *list, void *value)
{
//声明节点
listNode *node;
//分配节点内存, 分配不成功, 则返回 NULL
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
//设置节点的值
node->value = value;
//如果链表为空
if (list->len == 0) {
//初始化链表首节点和尾节点都为当前节点
list->head = list->tail = node;
//当前节点的前一个节点和后一个节点都为 NULL
node->prev = node->next = NULL;
} else {
//链表不为空
//设置原尾节点为当前节点的前一个节点
node->prev = list->tail;
//设置尾节点为 NULL
node->next = NULL;
//链表原尾节点的下一个节点为当前节点
list->tail->next = node;
//更新链表的为节点为当前节点
list->tail = node;
}
//节点长度加 1
list->len++;
//返回链表指针
return list;
}
2.6 根据原有节点指定位置插入新节点 listInsertNode
//根据原有节点指定位置插入新节点
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
//声明新的节点
listNode *node;
//新节点分配内存
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
//设置新节点的值
node->value = value;
//如果插入old_node的后面
if (after) {
//新节点的上一个节点设置为旧节点
node->prev = old_node;
//新节点的下一个节点设置为旧节点的下一个节点
node->next = old_node->next;
//如果旧节点是尾节点
if (list->tail == old_node) {
//重置当前节点为尾节点
list->tail = node;
}
} else {
//如果插入old_node的前面
//新节点的下一个节点为旧节点
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;
}
2.7 删除节点 listDelNode
//删除节点, 主要是各种链表操作哈
void listDelNode(list *list, listNode *node)
{
//如果当前节点的前一个节点不为 NULL
if (node->prev)
//设置前一个节点的下一个节点为当前节点的下一个节点
node->prev->next = node->next;
else
//要删除的节点为首节点, 直接将head设置为下一个节点
list->head = node->next;
//如果要删除的节点的下一个节点不为 NULL
if (node->next)
//将要删除节点的下一个节点的前一个节点, 重置为当前节点的前一个节点
node->next->prev = node->prev;
else
//要删除的节点是尾节点, 设置前一个节点为尾节点
list->tail = node->prev;
//如果free函数存在, 释放节点value的内存
if (list->free) list->free(node->value);
//释放节点内存
zfree(node);
//链表长度减 1
list->len--;
}
2.8 获取链表迭代器指针 listGetIterator
//获取链表迭代器指针
listIter *listGetIterator(list *list, int direction)
{
//声明迭代器
listIter *iter;
//分配迭代器内存
if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
//如果是前面开始遍历
if (direction == AL_START_HEAD)
//遍历节点从head开始
iter->next = list->head;
else
//从后面开始遍历
iter->next = list->tail;
//设置遍历方向
iter->direction = direction;
//返回指针
return iter;
}
2.9 释放迭代器对象内存 listReleaseIterator
//释放迭代器对象
void listReleaseIterator(listIter *iter) {
zfree(iter);
}
2.10 设置迭代器
//设置迭代器, 指定链表从head开始遍历
void listRewind(list *list, listIter *li) {
li->next = list->head;
li->direction = AL_START_HEAD;
}
//设置迭代器, 指定链表从 tail 开始遍历
void listRewindTail(list *list, listIter *li) {
li->next = list->tail;
li->direction = AL_START_TAIL;
}
2.11 获取迭代器下一个节点 listNext
//获取迭代器下一个节点
listNode *listNext(listIter *iter)
{
//获取下一个节点
listNode *current = iter->next;
//下一个节点不为 NULL
if (current != NULL) {
//如果是从头开始遍历
if (iter->direction == AL_START_HEAD)
//遍历节点更新为下一个节点
iter->next = current->next;
else
//从尾部开始遍历, 遍历节点更新为上一个节点
iter->next = current->prev;
}
//返回当前节点
return current;
}
2.12 复制链表 listDup
//复制链表
list *listDup(list *orig)
{
//声明复制的链表
list *copy;
//声明迭代器
listIter iter;
//
listNode *node;
//创建空链表, 为 NULL, 则直接返回 NULL
if ((copy = listCreate()) == NULL)
return NULL;
//继承原链表的相关函数
copy->dup = orig->dup;
copy->free = orig->free;
copy->match = orig->match;
//设置迭代器从头开始遍历
listRewind(orig, &iter);
//获取下一个节点
while((node = listNext(&iter)) != NULL) {
//值指针
void *value;
//如果有复制value的函数
if (copy->dup) {
//用函数拷贝值
value = copy->dup(node->value);
//如果为空, 则释放要拷贝的链表并且返回 NULL
if (value == NULL) {
listRelease(copy);
return NULL;
}
} else
//没有拷贝函数, 则直接获取值
value = node->value;
//添加值添加到copy链表的尾部, 内存分配失败则释放copy链表的内存并返回NULL
if (listAddNodeTail(copy, value) == NULL) {
listRelease(copy);
return NULL;
}
}
//返回拷贝的链表
return copy;
}
2.13 根据key查节点 listSearchKey
//根据key查节点
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;
}
}
}
//没找到, 则返回 NULL
return NULL;
}
2.14 获取指定下标的节点 listIndex
//获取指定下标的节点
listNode *listIndex(list *list, long index) {
listNode *n;
//如果 index 小于 0 , 则从后面遍历
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;
}
2.15 头尾节点互换
//将链表尾节点变头节点
void listRotateTailToHead(list *list) {
//长度小于等于一个节点, 不处理
if (listLength(list) <= 1) return;
/* Detach current tail */
//获取链表尾节点
listNode *tail = list->tail;
//重置尾节点为上一个节点
list->tail = tail->prev;
//重置新的尾节点的下一个节点为NULL
list->tail->next = NULL;
/* Move it as head */
//设置head的前一个节点为 tail
list->head->prev = tail;
//设置 tail 前一个节点为 NULL
tail->prev = NULL;
//更新 tail 的下一个节点为链表的 head
tail->next = list->head;
//重置 head 节点为 tail
list->head = tail;
}
/* Rotate the list removing the head node and inserting it to the tail. */
//将头节点变尾节点, 代码基本同 listRotateTailToHead 一致
void listRotateHeadToTail(list *list) {
if (listLength(list) <= 1) return;
listNode *head = list->head;
/* Detach current head */
list->head = head->next;
list->head->prev = NULL;
/* Move it as tail */
list->tail->next = head;
head->next = NULL;
head->prev = list->tail;
list->tail = head;
}
2.16 将链表o拼接到链表l后面
//将链表o拼接到链表l后面
void listJoin(list *l, list *o) {
//如果要合并的链表长度为 0, 则不处理
if (o->len == 0) return;
//设置 o 的头节点的前节点为 l 的尾节点
o->head->prev = l->tail;
//如果l的尾节点不为NULL
if (l->tail)
//设置 l 的尾节点的下一个节点为 o 的头节点
l->tail->next = o->head;
else
//如果l的尾节点为NULL, 则直接用o的首节点作用l的首节点
l->head = o->head;
//更新 l 的尾节点
l->tail = o->tail;
//更新l链表的长度
l->len += o->len;
/* Setup other as an empty list. */
//清空o链表
o->head = o->tail = NULL;
o->len = 0;
}
3 小结
链表超级简单, 实现上也跟java的LinkedList 差不多.
个人微信: wolfleong
微信公众号: wolfleong