redis源码学习_链表
Posted abc_begin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis源码学习_链表相关的知识,希望对你有一定的参考价值。
redis的链表是双向链表,该链表不带头结点,具体如下:
主要总结一下adlist.c和adlist.h里面的关键结构体和函数。
链表节点结构如下:
1 /* 2 * 双端链表节点 3 */ 4 typedef struct listNode { 5 6 // 前置节点 7 struct listNode *prev; //如果是list的头结点,则prev指向NULL 8 9 // 后置节点 10 struct listNode *next;//如果是list尾部结点,则next指向NULL 11 12 // 节点的值 13 void *value; 14 15 } listNode;
链表结构如下:
1 /* 2 * 双端链表结构 3 */ 4 typedef struct list { 5 6 // 表头节点 7 listNode *head; 8 9 // 表尾节点 10 listNode *tail; 11 12 // 节点值复制函数 13 void *(*dup)(void *ptr); 14 15 // 节点值释放函数 16 void (*free)(void *ptr); 17 18 // 节点值对比函数 19 int (*match)(void *ptr, void *key); 20 21 // 链表所包含的节点数量,有了这个我们获得链表长度的时间复杂度就是O(1)了 22 unsigned long len; 23 24 } list;
链表迭代器的结构如下:
1 /* 2 * 双端链表迭代器 3 */ 4 typedef struct listIter { 5 6 // 当前迭代到的节点 7 listNode *next; 8 9 // 迭代的方向 10 int direction; //取值AL_START_HEAD等 11 12 } listIter;
里面涉及的函数中,增、删的比较简单,就是结构里面没有带头结点,所以需要单独判断一下头结点的特殊情况。另外对于尾节点的操作也需要考虑一下特殊情况。
listAddNodeHead:将一个包含给定值的新节点添加到给定链表的表头
1 /* Add a new node to the list, to head, contaning the specified \'value\' 2 * pointer as value. 3 * 4 * On error, NULL is returned and no operation is performed (i.e. the 5 * list remains unaltered). 6 * On success the \'list\' pointer you pass to the function is returned. */ 7 /* 8 * 将一个包含有给定值指针 value 的新节点添加到链表的表头 9 * 10 * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL 11 * 12 * 如果执行成功,返回传入的链表指针 13 * 14 * T = O(1) 15 */ 16 list *listAddNodeHead(list *list, void *value) 17 { 18 listNode *node; 19 20 // 为节点分配内存 21 if ((node = zmalloc(sizeof(*node))) == NULL) 22 return NULL; 23 24 // 保存值指针 25 node->value = value; 26 27 // 添加节点到空链表 28 if (list->len == 0) { 29 list->head = list->tail = node; 30 node->prev = node->next = NULL; 31 // 添加节点到非空链表 32 } else { 33 node->prev = NULL; 34 node->next = list->head; 35 list->head->prev = node; 36 list->head = node; 37 } 38 39 // 更新链表节点数 40 list->len++; 41 42 return list; 43 }
listAddNodeTail:将一个包含给定值的新节点添加到给定链表的表尾
1 /* Add a new node to the list, to tail, containing the specified \'value\' 2 * pointer as value. 3 * 4 * On error, NULL is returned and no operation is performed (i.e. the 5 * list remains unaltered). 6 * On success the \'list\' pointer you pass to the function is returned. */ 7 /* 8 * 将一个包含有给定值指针 value 的新节点添加到链表的表尾 9 * 10 * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL 11 * 12 * 如果执行成功,返回传入的链表指针 13 * 14 * T = O(1) 15 */ 16 list *listAddNodeTail(list *list, void *value) 17 { 18 listNode *node; 19 20 // 为新节点分配内存 21 if ((node = zmalloc(sizeof(*node))) == NULL) 22 return NULL; 23 24 // 保存值指针 25 node->value = value; 26 27 // 目标链表为空 28 if (list->len == 0) { 29 list->head = list->tail = node; 30 node->prev = node->next = NULL; 31 // 目标链表非空 32 } else { 33 node->prev = list->tail; 34 node->next = NULL; 35 list->tail->next = node; 36 list->tail = node; 37 } 38 39 // 更新链表节点数 40 list->len++; 41 42 return list; 43 }
listInsertNode:将一个包含给定值的新节点添加到给定节点的之前或者之后
1 /* 2 * 创建一个包含值 value 的新节点,并将它插入到 old_node 的之前或之后 3 * 4 * 如果 after 为 0 ,将新节点插入到 old_node 之前。 5 * 如果 after 为 1 ,将新节点插入到 old_node 之后。 6 * 7 * T = O(1) 8 */ 9 list *listInsertNode(list *list, listNode *old_node, void *value, int after) { 10 listNode *node; 11 12 // 创建新节点 13 if ((node = zmalloc(sizeof(*node))) == NULL) 14 return NULL; 15 16 // 保存值 17 node->value = value; 18 19 // 将新节点添加到给定节点之后 20 if (after) { 21 node->prev = old_node; 22 node->next = old_node->next; 23 // 给定节点是原表尾节点 24 if (list->tail == old_node) { 25 list->tail = node; 26 } 27 // 将新节点添加到给定节点之前 28 } else { 29 node->next = old_node; 30 node->prev = old_node->prev; 31 // 给定节点是原表头节点 32 if (list->head == old_node) { 33 list->head = node; 34 } 35 } 36 37 // 更新新节点的前置指针 38 if (node->prev != NULL) { 39 node->prev->next = node; 40 } 41 // 更新新节点的后置指针 42 if (node->next != NULL) { 43 node->next->prev = node; 44 } 45 46 // 更新链表节点数 47 list->len++; 48 49 return list; 50 }
listDelNode:从链表中删除给定节点
1 /* Remove the specified node from the specified list. 2 * It\'s up to the caller to free the private value of the node. 3 * 4 * This function can\'t fail. */ 5 /* 6 * 从链表 list 中删除给定节点 node 7 * 8 * 对节点私有值(private value of the node)的释放工作由调用者进行。 9 * 10 * T = O(1) 11 */ 12 void listDelNode(list *list, listNode *node) 13 { 14 // 调整前置节点的指针 15 if (node->prev) 16 node->prev->next = node->next; 17 else 18 list->head = node->next; 19 20 // 调整后置节点的指针 21 if (node->next) 22 node->next->prev = node->prev; 23 else 24 list->tail = node->prev; 25 26 // 释放值 27 if (list->free) list->free(node->value); 28 29 // 释放节点 30 zfree(node); 31 32 // 链表数减一 33 list->len--; 34 }
listGetIterator:创建一个链表迭代器,并根据传入的direction来返回链表的头或尾节点
1 /* Returns a list iterator \'iter\'. After the initialization every 2 * call to listNext() will return the next element of the list. 3 * 4 * This function can\'t fail. */ 5 /* 6 * 为给定链表创建一个迭代器, 7 * 之后每次对这个迭代器调用 listNext 都返回被迭代到的链表节点 8 * 9 * direction 参数决定了迭代器的迭代方向: 10 * AL_START_HEAD :从表头向表尾迭代 11 * AL_START_TAIL :从表尾想表头迭代 12 * 13 * T = O(1) 14 */ //获取列表list的首部阶段或者尾部结点 15 listIter *listGetIterator(list *list, int direction) 16 { 17 // 为迭代器分配内存 18 listIter *iter; 19 if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL; 20 21 // 根据迭代方向,设置迭代器的起始节点 22 if (direction == AL_START_HEAD) 23 iter->next = list->head; 24 else 25 iter->next = list->tail; 26 27 // 记录迭代方向 28 iter->direction = direction; 29 30 return iter; 31 }
listNext:拿到下一个迭代器节点
1 /* 2 * 返回迭代器当前所指向的节点。 3 * 4 * 删除当前节点是允许的,但不能修改链表里的其他节点。 5 * 6 * 函数要么返回一个节点,要么返回 NULL ,常见的用法是: 7 * 8 * iter = listGetIterator(list,<direction>); 9 * while ((node = listNext(iter)) != NULL) { 10 * doSomethingWith(listNodeValue(node)); 11 * } 12 * 13 * T = O(1) 14 */ 15 listNode *listNext(listIter *iter) 16 { 17 listNode *current = iter->next; 18 19 if (current != NULL) { 20 // 根据方向选择下一个节点 21 if (iter->direction == AL_START_HEAD) 22 // 保存下一个节点,防止当前节点被删除而造成指针丢失 23 iter->next = current->next; 24 else 25 // 保存下一个节点,防止当前节点被删除而造成指针丢失 26 iter->next = current->prev; 27 } 28 29 return current; 30 }
前面说了那么多操作,其实就是为了把它们组合起来使用,比如下面这个函数,就是上面的综合体。
listDup:复制链表
1 /* Duplicate the whole list. On out of memory NULL is returned. 2 * On success a copy of the original list is returned. 3 * 4 * The \'Dup\' method set with listSetDupMethod() function is used 5 * to copy the node value. Otherwise the same pointer value of 6 * the original node is used as value of the copied node. 7 * 8 * The original list both on success or error is never modified. */ 9 /* 10 * 复制整个链表。 11 * 12 * 复制成功返回输入链表的副本, 13 * 如果因为内存不足而造成复制失败,返回 NULL 。 14 * 15 * 如果链表有设置值复制函数 dup ,那么对值的复制将使用复制函数进行, 16 * 否则,新节点将和旧节点共享同一个指针。 17 * 18 * 无论复制是成功还是失败,输入节点都不会修改。 19 * 20 * T = O(N) 21 */ 22 list *listDup(list *orig) 23 { 24 list *copy; 25 listIter *iter; 26 listNode *node; 27 28 // 创建新链表 29 if ((copy = listCreate()) == NULL) 30 return NULL; 31 32 // 设置节点值处理函数 33 copy->dup = orig->dup; 34 copy->free = orig->free; 35 copy->match = orig->match; 36 37 // 迭代整个输入链表 38 iter = listGetIterator(orig, AL_START_HEAD); 39 while((node = listNext(iter)) != NULL) { 40 void *value; 41 42 // 复制节点值到新节点 43 if (copy->dup) { 44 value = copy->dup(node->value); 45 if (value == NULL) { 46 listRelease(copy); 47 listReleaseIterator(iter); 48 return NULL; 49 } 50 } else 51 value = node->value; 52 53 // 将节点添加到链表 54 if (listAddNodeTail(copy, value) == NULL) { 55 listRelease(copy); 56 listReleaseIterator(iter); 57 return NULL; 58 } 59 } 60 61 // 释放迭代器 62 listReleaseIterator(iter); 63 64 // 返回副本 65 return copy; 66 }
最后再提一个旋转函数listRotate,也没有什么需要过多解释的。
1 /* Rotate the list removing the tail node and inserting it to the head. */ 2 /* 3 * 取出链表的表尾节点,并将它移动到表头,成为新的表头节点。 4 * 5 * T = O(1) 6 */ 7 void listRotate(list *list) { 8 listNode *tail = list->tail; 9 10 if (listLength(list) <= 1) return; 11 12 /* Detach current tail */ 13 // 取出表尾节点 14 list->tail = tail->prev; 15 list->tail->next = NULL; 16 17 /* Move it as head */ 18 // 插入到表头 19 list->head->prev = tail; 20 tail->prev = NULL; 21 tail->next = list->head; 22 list->head = tail; 23 }
总体来说,链表的数据结构是属于比较简单的。
以上是关于redis源码学习_链表的主要内容,如果未能解决你的问题,请参考以下文章