一、简介
上一节,介绍了sds结构,接下来将介绍下redis的adlist(又称链表).redis的链表采用双向链表方式进行实现,代码位于src/adlist.h、src/adlist.c .
二、数据结构
C语言数据结构中双向链表结构由节点(listNode)、迭代器(listIterator)、容器(list)组成,redis也是如此。
节点
typedef struct listNode { struct listNode *prev; struct listNode *next; void *value; } listNode;
value:节点的内容 prev和next分别代表指向上一个节点与下一个节点的指针。
迭代器
typedef struct listIter { listNode *next; int direction; } listIter;
next:下一个节点指针 direction:遍历方向
容器
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;
head:表头指针 tail表尾指针 dup:复制函数 free:释放函数 match:遍历查找函数 len:链表长度
三、链表操作方法
上面简单地介绍了下链表的结构,下面将介绍链表的相关函数。链表的操作函数,相信大家在C语言数据结构都有接触过,实现代码比较简单。
listCreate: 创建一个list,并初始化,然后返回list对象。
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; }
listRelease: 与listCreate对应,该函数实现链表释放功能。
void listRelease(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); zfree(current); current = next; } zfree(list); }
细心的读者可能注意到 list->free函数,这个函数是用于释放节点内容的空间。
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: 与listAddNodeHead功能类似,添加数据至表尾。
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: 将新节点添加至指定的节点之前/之后
//若after=0 ,将新节点插入到 old_node 之前。 //若after=1 ,将新节点插入到 old_node 之后。 list *listInsertNode(list *list, listNode *old_node, void *value, int after) { listNode *node; if ((node = zmalloc(sizeof(*node))) == NULL) return NULL; node->value = value; //修改node前后指针指向 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; } } //如果新插入元素的前节点非空,则修改前节点的next指针指向 if (node->prev != NULL) { node->prev->next = node; } //如果新插入元素的后节点非空,则修改后节点的prev指针指向 if (node->next != NULL) { node->next->prev = node; } list->len++; return list; }
listDelNode: 删除指定节点操作
void listDelNode(list *list, listNode *node) { //修改node前节点的next指向 if (node->prev) node->prev->next = node->next; else list->head = node->next; //修改node前节点的prev指向 if (node->next) node->next->prev = node->prev; else list->tail = node->prev; //释放node节点 if (list->free) list->free(node->value); zfree(node); //len-1 list->len--; }
listGetIterator: 获取迭代器
//direction:有两个值 //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; }
listRewind: 重置链表指针到表头。与之对应,listRewindTail的功能为重置指针至表尾。
void listRewind(list *list, listIter *li) { li->next = list->head; li->direction = AL_START_HEAD; }
listNext: 该函数非常重要,在很多链表操作都有使用,实现了链表遍历的功能。
listNode *listNext(listIter *iter) { //获取下一个指针 listNode *current = iter->next; //如果current非空,则根据direction值,修改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 = orig->dup; copy->free = orig->free; copy->match = orig->match; //重置orig指针至表头 listRewind(orig, &iter); //遍历orig 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; }
listSearchKey:根据给出的key对链表进行查找
listNode *listSearchKey(list *list, void *key) { listIter iter; listNode *node; //重置指针 listRewind(list, &iter); //遍历 while((node = listNext(&iter)) != NULL) { //对比value if (list->match) { if (list->match(node->value, key)) { return node; } } else { if (key == node->value) { return node; } } } return NULL; }
listIndex: 根据给出的下标,查找相应的节点。
//根据给出的下标进行搜索 //index>=0: 顺序遍历,从表头往表尾进行遍历 //index<0: 逆向遍历,从表尾往表头进行遍历 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; }
四、总结
1. redis的链表采用了双向非循环链表设计。
2. redis对链表并没有做太大的改造,与C语言数据结构链表功能大同小异。