链表作为经典且常用的数据结构,在很多高级编程语言中都内置了链表这种数据额结构,但是Redis是有C语言实现,并没有内置这种数据结构,所以Redis自身构建了链表。
节点和链表的实现
listNode
结构表示为:
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;
dup
函数用于复制链表节点保存的值。当链表需要复制、移植等操作的时候,便会用到dup
函数来复制节点的值。因为元素的值可能是一个自定义的结构体或者其他类型的数据结构,存在着指针等成员,如果直接用=
号复制会造成隐患(可以重构=
来改善)。如果不想使用dup
函数,可以将dup
赋值为NULL
,这样就可以直接使用默认操作符=来复制节点内容。free
函数用于释放链表节点保存的值。也是为了一些指针成员变量而实现的,一般指针成员都需要手动分配一块内存来使用,若是不相应的调用free
来释放内存,将会导致内存泄露。同dup
一样,free
在赋值NULL
之后,便会调用Redis自带的zfree
函数来释放。match
函数用于比较链表节点保存的值与另一值是否相等。在通过一个值在链表查找是否拥有该元素时,如果match
函数没有被赋值为NULL
,则通过match
函数来比较两个值是否相等。
下图展示由3个listNode组成的list
Redis链表特性:
- 双向:可以在时间复杂度为
O(1)
的情况下获取某个节点的前置节点和后置节点
-len
属性:可在时间复杂度为O(1)
的情况下获取链表长度 head
、tail
指针:方便访问头尾节点- 多态:
void *
保存节点的值,并且dup
、free
、match
可为节点值设置类型特定的函数,是链表可以保存多种类型
关于链表的一些源码
Redis链表这部分算是比较简单的,源码中一些API的实现的代码也容易看懂。以下对一些源码做一些简单的说明,不感兴趣的可以不看。
listIter:
typedef struct listIter {
listNode *next; // 指向下一个元素的指针
int direction; // 遍历方向
} listIter;
#define AL_START_HEAD 0
#define AL_START_TAIL 1
实现一个链表的迭代器,是为了方便的遍历整个链表。direction
的值取为AL_START_HEAD
或者AL_START_TAIL
,分别代表从头部开始遍历,及从尾部遍历。比如说当direction
为AL_START_HEAD
时,next跟listNode中的next
值相同,当direction
为AL_START_TAIL
时,next
跟listNode
中的prev
值相同。
listCreate
list *listCreate(void)
{
struct list *list; // 链表的结构体
if ((list = zmalloc(sizeof(*list))) == NULL) // 分配空间
return NULL; // 出错是返回NULL,否则返回链表指针
list->head = list->tail = NULL;
list->len = 0;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list;
}
创建并初始化一个链表。可用AlFreeList()
函数释放创建的链表。但是调用AlFreeList()
之前需要释放每个节点。
listEmpty
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); //判断是否有定义free函数,如果有,直接使用
zfree(current); // 没有定义free,则使用内部封装好的zfree释放节点
current = next; // 指向下一个节点
}
list->head = list->tail = NULL;
list->len = 0;
}
清空整个链表但不破坏链表本省,从head
开始,通过链表长度len
迭代释放节点。
listRelease
void listRelease(list *list)
{
listEmpty(list); // 清空整个链表
zfree(list); // 释放链表本身
}
释放整个链表
listAddNodeHead
list *listAddNodeHead(list *list, void *value)
{
listNode *node;
// 创建一个节点并申请内存
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == 0) { // 链表长度为0的情景
list->head = list->tail = node; // 头尾指针都指向该节点
node->prev = node->next = NULL; // 节点的前后指针指向NULL
} else { // 链表长度非0的情景
node->prev = NULL;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
list->len++; // 增加链表长度
return list;
}
为一个元素申请一个节点,并添加到链表最前端。出错时,返回NULL
并且不执行任何操作(即链表保持不变)。成功时,返回传递给函数的list
指针。
listAddNodeTail
list *listAddNodeTail(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 = list->tail;
node->next = NULL;
list->tail->next = node;
list->tail = node;
}
list->len++;
return list;
}
方法和listAddNodeHead
一样,listAddNodeTail
将节点添加到最末端。
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;
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;
}
为一个元素创建节点,并插入在指定节点前或者指定节点后。after参数来控制节点插入的位置,after
为0则插入在指定节点前,after
为非0则插入在指定节点后。
listDelNode
void listDelNode(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;
if (list->free) list->free(node->value);
zfree(node); //释放节点
list->len--; //链表长度自减
}
从链表当中删除指定的一个节点。
listGetIterator、listReleaseIterator、listRewind、listRewindTail、listNext
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;
}
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;
}
五个函数全部跟迭代器相关,分别为创建迭代器、销毁迭代器、重置迭代器到链表头部、重置迭代器到链表尾部、获取迭代器下一个节点。
listNode:返回迭代器的下一个元素。 使用listDelNode()
删除当前返回的元素是有效的,但不删除其他元素。该函数返回指向列表下一个元素的指针,如果没有更多元素,则返回NULL
listDup
list *listDup(list *orig)
{
list *copy;
listIter iter;
listNode *node;
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;
if (copy->dup) { // 判断是否有自定义复制函数
value = copy->dup(node->value);
if (value == NULL) {
listRelease(copy);
return NULL;
}
} else
value = node->value;
if (listAddNodeTail(copy, value) == NULL) {
listRelease(copy);
return NULL;
}
}
return copy;
}
复制整个列表。 在内存不足时返回NULL。成功时,将返回原始列表的副本。
listSearchKey
listNode *listSearchKey(list *list, void *key)
{
listIter iter;
listNode *node;
listRewind(list, &iter); // 创建迭代器,迭代访问源链表
while((node = listNext(&iter)) != NULL) {
if (list->match) { // 如果定义了match函数则只用
if (list->match(node->value, key)) {
return node;
}
} else { // 没有定义match,则使用=
if (key == node->value) {
return node;
}
}
}
return NULL;
}
在列表中搜索与给定键匹配的节点。成功时,返回第一个匹配的节点指针(从头开始搜索)。 如果不存在匹配节点,则返回NULL。
listIndex
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;
}
返回指定的从零开始的索引处的元素,其中0是头部,1是头部旁边的元素,依此类推。 使用负整数以便从尾部计数,-1是最后一个元素,-2是倒数第二个,依此类推。 如果索引超出范围,则返回NULL。
listRotate
void listRotate(list *list) {
listNode *tail = list->tail;
if (listLength(list) <= 1) return; // 链表长度小于或等于1,直接返回
/* 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;
}
旋转列表,删除尾节点并将其插入头部
listJoin
void listJoin(list *l, list *o) {
if (o->head)
o->head->prev = l->tail;
if (l->tail)
l->tail->next = o->head;
else
l->head = o->head;
if (o->tail) l->tail = o->tail;
l->len += o->len;
/* Setup other as an empty list. */
o->head = o->tail = NULL;
o->len = 0;
}
在列表’l’的末尾添加列表’o’的所有元素。 列表’o’置为空,但有效。