redis源码分析2---结构体---链表
因为redis包含大量的键值对,redis中列表键的底层实现之一就是链表;当一个链表键包含了数量比较多的
元素,又或者列表中包含的元素都是比较长的字符串时,redis就会使用链表作为底层实现;
另外链表做外数据结构中最常使用的结构,如何高效的实现和值得我们学习;我之前在项目中大量使用了链表
但是感觉总是写的不大好;redis都是大神们写出来的,值得好好学学!
redis源码分析1中讲的SDS部分结构:先总说SDS的实现,再分析源代码,这种结构将贯穿整个redis源码分析过程;
希望这样能方便大家理解。
1 为什么使用链表和链表定义。
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活的调整链表的长度。底层列表键的实现方式之一就是链表。
节点定义如下
是一个双端链表
链表的结构定义如下
dup函数用于复制链表节点所保存的值
free函数用于释放链表节点所保存的值
match函数则用于对比链表节点所保存的值和另一个输入值是否相等;
综合看起来,redis中的链表结构如下:我们以一个list结构和三个listnode结构组成的链表为例。
2 链表的API
3 源代码分析
3.1 迭代器
为了便于访问,在adlist.h中定义了双端链表迭代器,方便访问整个链表;
3.2 直接通过属性获得的函数具体实现办法
3.3 链表的初始化和释放
/* * 创建一个新的链表 * * 创建成功返回链表,失败返回 NULL 。 * * T = O(1) */ 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; } /* * 释放整个链表,以及链表中所有节点 * * T = O(N) */ 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); }
3.4 在头部,尾部添加节点或者删除节点
/* * 将一个包含有给定值指针 value 的新节点添加到链表的表头 * * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL * * 如果执行成功,返回传入的链表指针 * * T = O(1) */ 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; } /* * 将一个包含有给定值指针 value 的新节点添加到链表的表尾 * * 如果为新节点分配内存出错,那么不执行任何动作,仅返回 NULL * * 如果执行成功,返回传入的链表指针 * * T = O(1) */ 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; } /* * 创建一个包含值 value 的新节点,并将它插入到 old_node 的之前或之后 * * 如果 after 为 0 ,将新节点插入到 old_node 之前。 * 如果 after 为 1 ,将新节点插入到 old_node 之后。 * * T = O(1) */ 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; } /* * 从链表 list 中删除给定节点 node * * 对节点私有值(private value of the node)的释放工作由调用者进行。 * * T = O(1) */ 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--; }
3.5 迭代器和迭代器访问节点
链表中使用了迭代器来访问节点,可以仔细分析迭代器是如何访问的,学习这种访问链表节点的方式;
/* * 为给定链表创建一个迭代器, * 之后每次对这个迭代器调用 listNext 都返回被迭代到的链表节点 * * direction 参数决定了迭代器的迭代方向: * AL_START_HEAD :从表头向表尾迭代 * AL_START_TAIL :从表尾想表头迭代 * * T = O(1) */ 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 */ /* * 释放迭代器 * * T = O(1) */ void listReleaseIterator(listIter *iter) { zfree(iter); } //设置迭代器的方向 /* * 将迭代器的方向设置为 AL_START_HEAD , * 并将迭代指针重新指向表头节点。 * * T = O(1) */ void listRewind(list *list, listIter *li) { li->next = list->head; li->direction = AL_START_HEAD; } /* * 将迭代器的方向设置为 AL_START_TAIL , * 并将迭代指针重新指向表尾节点。 * * T = O(1) */ void listRewindTail(list *list, listIter *li) { li->next = list->tail; li->direction = AL_START_TAIL; } 迭代器访问 * 返回迭代器当前所指向的节点。 * * 删除当前节点是允许的,但不能修改链表里的其他节点。 * * 函数要么返回一个节点,要么返回 NULL ,常见的用法是: * * iter = listGetIterator(list,<direction>); * while ((node = listNext(iter)) != NULL) { * doSomethingWith(listNodeValue(node)); * } * * T = O(1) */ 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; }
3.6 复制,查找,旋转
/* * 复制整个链表。 * * 复制成功返回输入链表的副本, * 如果因为内存不足而造成复制失败,返回 NULL 。 * * 如果链表有设置值复制函数 dup ,那么对值的复制将使用复制函数进行, * 否则,新节点将和旧节点共享同一个指针。 * * 无论复制是成功还是失败,输入节点都不会修改。 * * T = O(N) */ 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; // 迭代整个输入链表 iter = listGetIterator(orig, AL_START_HEAD); while((node = listNext(iter)) != NULL) { void *value; // 复制节点值到新节点 if (copy->dup) { value = copy->dup(node->value); if (value == NULL) { listRelease(copy); listReleaseIterator(iter); return NULL; } } else value = node->value; // 将节点添加到链表 if (listAddNodeTail(copy, value) == NULL) { listRelease(copy); listReleaseIterator(iter); return NULL; } } // 释放迭代器 listReleaseIterator(iter); // 返回副本 return copy; } /* Search the list for a node matching a given key. * The match is performed using the 'match' method * set with listSetMatchMethod(). If no 'match' method * is set, the 'value' pointer of every node is directly * compared with the 'key' pointer. * * On success the first matching node pointer is returned * (search starts from head). If no matching node exists * NULL is returned. */ /* * 查找链表 list 中值和 key 匹配的节点。 * * 对比操作由链表的 match 函数负责进行, * 如果没有设置 match 函数, * 那么直接通过对比值的指针来决定是否匹配。 * * 如果匹配成功,那么第一个匹配的节点会被返回。 * 如果没有匹配任何节点,那么返回 NULL 。 * * T = O(N) */ listNode *listSearchKey(list *list, void *key) { listIter *iter; listNode *node; // 迭代整个链表 iter = listGetIterator(list, AL_START_HEAD); while((node = listNext(iter)) != NULL) { // 对比 if (list->match) { if (list->match(node->value, key)) { listReleaseIterator(iter); // 找到 return node; } } else { if (key == node->value) { listReleaseIterator(iter); // 找到 return node; } } } listReleaseIterator(iter); // 未找到 return NULL; } /* Return the element at the specified zero-based index * where 0 is the head, 1 is the element next to head * and so on. Negative integers are used in order to count * from the tail, -1 is the last element, -2 the penultimate * and so on. If the index is out of range NULL is returned. */ /* * 返回链表在给定索引上的值。 * * 索引以 0 为起始,也可以是负数, -1 表示链表最后一个节点,诸如此类。 * * 如果索引超出范围(out of range),返回 NULL 。 * * T = O(N) */ 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; } /* Rotate the list removing the tail node and inserting it to the head. */ /* * 取出链表的表尾节点,并将它移动到表头,成为新的表头节点。 * * T = O(1) */ 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; }