redis中的adlist实质上是一个双向链表。里面实现了自己的iterator(包含正负方向)。作者对于链表写的真的是流畅,看得赏心悦目。没有一句多余的,很值得学习。
数据结构
总体来说分为三个数据类型。
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;
listNode结构体。每个节点的数据结构
typedef struct listNode {
struct listNode *prev;//前指正
struct listNode *next;//后指针
void *value;//存储的数据
} listNode;
iterator结构体
typedef struct listIter {
listNode *next;//下一个节点
int direction;//方向 利用宏定义来判断
} listIter;
/* Directions for iterators */
#define AL_START_HEAD 0 //从前往后即为0
#define AL_START_TAIL 1//从后往前
垃圾字体画出来的图
在所提供的实现函数中,个人认为创建,插入,删除,复制,查找以及迭代器的操作比较重要。
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;
}
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函数则使用free清理value
zfree(current);//删除节点
current = next;//循环
}
list->head = list->tail = NULL;//链表初始化
list->len = 0;
}
listRelease清空链表元素及自身,调用listEmpty
/* Free the whole list.
*
* This function can't fail. */
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) {//若位空链表
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;
}
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;
}
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) {//若after为真则表示新节点插在old_node后否则插在其前面
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) {//若不是头节点,要调整前面节点的next指针
node->prev->next = node;
}
if (node->next != NULL) {//若不是尾节点,要调整后面节点的pre指针
node->next->prev = node;
}
list->len++;//长度加一
return list;
}
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);//若定义了free 释放value空间
zfree(node);//释放节点
list->len--;//长度减1
}
listGetIterator 获取迭代器
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;
}
listNext 迭代器返回当前值,并且向后移
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 复制一个相同的链表,返回为新的链表 (dup dup2为linux中 文件描述符的复制的函数名)
list *listDup(list *orig)
{
list *copy;
listIter iter;
listNode *node;
if ((copy = listCreate()) == NULL) //
return NULL;
copy->dup = orig->dup; //拷贝相应的api函数
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);//失败则调用释放内存的函数释放,并返回NULL
return NULL;
}
}
return copy;
}
listSearchKey 根据可以key值找节点
listNode *listSearchKey(list *list, void *key)
{
listIter iter; //同样使用的不是指针,省去释放烦恼。
listNode *node;
listRewind(list, &iter);//取得反向迭代器 与c++类似
while((node = listNext(&iter)) != NULL) {
if (list->match) {//若有math函数则调用
if (list->match(node->value, key)) {
return node;
}
} else {
if (key == node->value) {//否则直接判等
return node;
}
}
}
return NULL;//若找不到则返回空
}
对于这一数据结构的学习,复习了对链表的基本操作。