Redis系统使用C语言自己实现了一个链表,主要相关代码为adlist.h跟adlist.c文件当中。
链表的实现很简单,且跟传统数据结构中的链表实现基本一致,只不过多了一些辅助的使用方法。接下来总结从结构体开始分析记录,再分析相关函数。
listNode:
typedef struct listNode {
struct listNode *prev;//指向前一个元素的指针
struct listNode *next;//指向下一个元素的指针
void *value;//元素内容的指针
} listNode;
跟传统链表相同,有着前后元素指针,只不过元素的实际内容使用void指针存储,意味着存储内容的格式不再有限制,不需要因为存储值的类型变更而声明多个链表。
listIter:
typedef struct listIter {
listNode *next;//指向下一个元素的指针
int direction;//遍历方向
} listIter;
#define AL_START_HEAD 0
#define AL_START_TAIL 1
这里是实现了链表的迭代器(iterator),用于方便的遍历、访问整个链表。其中next为指向下一个元素的指针,direction则是遍历的方便。direction的值取为AL_START_HEAD或者AL_START_TAIL,分别代表从头部开始遍历,及从尾部遍历。比如说当direction为AL_START_HEAD时,next跟listNode中的next值相同,当direction为AL_START_TAIL时,next跟listNode中的prev值相同。
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函数来比较两个值是否相等。
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;
}
listCreate函数用于创建并初始化一个链表,其中zmalloc为redis自带的函数,zmalloc对malloc进行了一定的封装。
listEmpty函数:
void listEmpty(list *list)
{
unsigned long len;
unsigned *current, *next;
current = list->head;
len = list->len;
//迭代释放链表中的元素
while(len--)
{
next = current->next;
//判断是否有自定义free函数,并进行相应处理
if (list->free) list->free(current->value);
//释放指定节点
zfree(current);
//指向下一个节点
current = next;
}
//初始化
list->head = list->tail = NULL;
list->len = 0;
}
listEmpty函数用于清空整个链表,从head开始,通过迭代访问所有节点来释放所有节点,释放内存。
listRelease函数:
void listRelease(list *list)
{
listEmpty(list);//清空整个链表
zfree(lit);//释放链表本身
}
listRelease函数用于释放整个链表,包括链表头,zfree同zmalloc一样,都是redis自己封装的函数。
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;
} else {
node->prev = NULL;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
list->len++;//增加链表长度
return list;
}
listAddNodeHead函数为一个元素申请一个节点,并添加到链表最前端。实现逻辑不复杂,只要是有基础数据结构知识的都是可以一眼了解的。
listAddNodeTail:
list *listAddNodeTail(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;
} else {
node->prev = list->tail;
node->next = NULL;
list->tail->next = node;
list->tail = node;
}
list->len++;//增加链表长度
return list;
}
与上述的函数列斯,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;
}
listInsertNode函数为一个元素创建节点,并插入在指定节点前或者指定节点后。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;
//判断自定义free函数是否为空
if (list->free) list->free(node->value);
//释放节点
zfree(node);
//自减链表长度
list->len--;
}
listDelNode函数为从链表当中删除指定的一个节点。
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 = liest->head;
else
//从尾部开始遍历的迭代器
iter->next = list->tail;
iter->direction = direction;//更新迭代器的访问顺序属性
return iter;
}
void listReleaseIterator(listIter *iter) {
zfree(iter);
}
void listRewind(list *list, listIter *li) {
li->next = list->head;
li->direction = AL_START_HEAD;
}
void listRewindTail(list *lit, 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;
}
以上五个函数全部跟迭代器相关,分别为创建迭代器、销毁迭代器、重置迭代器到链表头部、重置迭代器到链表尾部、获取迭代器下一个节点。
listDup:
list *listDup(list *orig)
{
list *copy;
listIter iter;
listNode *node;
if ((copy = listCreate() == NULL)//创建一个新链表
return NULL;
copy->dup = orgi->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;
}
listDup函数为复制一个已知链表。
listSearchKey & listIndex & listRotate & listJoin:
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 {
if (key == node->value) {
return node;
}
}
}
return NULL;
}
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;
}
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;
}
void listJoin(list *l, list *o) {
//将o连边的头接到l链表尾部
if (o->head)
o->head->prev = l->tail;
//将l链表的尾部指向o链表的头部
if (l->tail)
l->tail->next = o->head;
else
l->head = o->head;
//更新l链表属性
l->tail = o->tail;
l->len += o->len;
/* Setup other as an empty list. */
o->head = o->tail = NULL;
o->len = 0;
}
以上四个函数为最后的list相关api了。
listSearchKey为在链表当中寻找指定元素的节点,若成功找到则返回该节点,若找不到则返回NULL。这里,若是自定义了match函数,则使用match函数来比较元素的值,否则则使用默认的'='操作符。
listIndex函数使用索引的方式访问链表当中的元素,类似数组的[index]或者向量里面的at(index),只不过listIndex多了一个从尾部计数返回的功能,跟python的数据相似,采用负数的索引,则从尾部开始计数,并返回节点。若索引超出界限则返回NULL。
listRotate函数用于将链表最后节点移到最开始节点。
listJoin函数用于链表的拼接,将o链表拼接与l链表的尾部。
以上便是redis的链表相关的所有代码,总结来说,redis的链表实现还是很精简、易懂的,只要有一定的数据结构跟C语言基础就可以很快读懂它的代码了。