Redis源码分析笔记3-redis的数据类型-链表

来源:互联网 发布:开淘宝护肤品店 编辑:程序博客网 时间:2024/06/07 13:37

简介:
本节主要和大家一起讨论redis五种基本类型中的链表。
分别从链表的定义、实现、迭代器的实现等几个方面讨论。


链表

链表节点的定义

Radis链表的实现在文件src/adlist.h和src/adlist.c中,链表节点的定义没有什么特殊点。如下所示:

/* * 双端链表节点 */typedef struct listNode {    // 前置节点    struct listNode *prev;    // 后置节点    struct listNode *next;    // 节点的值    void *value;} listNode;

双链表的实现

/* * 双端链表结构 */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、free、match三个函数指针。凭借经验猜测,这是为了实现不同类型的多态链表。我们继续往下看来印证我们的猜测。
代码继续往下看就看到了链表的多态的实现。如下图所示:

// 将链表 l 的值复制函数设置为 m// T = O(1)#define listSetDupMethod(l,m) ((l)->dup = (m))// 将链表 l 的值释放函数设置为 m// T = O(1)#define listSetFreeMethod(l,m) ((l)->free = (m))// 将链表的对比函数设置为 m// T = O(1)#define listSetMatchMethod(l,m) ((l)->match = (m))// 返回给定链表的值复制函数// T = O(1)#define listGetDupMethod(l) ((l)->dup)// 返回给定链表的值释放函数// T = O(1)#define listGetFree(l) ((l)->free)// 返回给定链表的值对比函数// T = O(1)#define listGetMatchMethod(l) ((l)->match)

图12
链表的比较常见的操作是用宏定义实现的,没有什么特殊的地方,如下图所示:

/* Functions implemented as macros */// 返回给定链表所包含的节点数量// T = O(1)#define listLength(l) ((l)->len)// 返回给定链表的表头节点// T = O(1)#define listFirst(l) ((l)->head)// 返回给定链表的表尾节点// T = O(1)#define listLast(l) ((l)->tail)// 返回给定节点的前置节点// T = O(1)#define listPrevNode(n) ((n)->prev)// 返回给定节点的后置节点// T = O(1)#define listNextNode(n) ((n)->next)// 返回给定节点的值// T = O(1)#define listNodeValue(n) ((n)->value)

图13
双端迭代器

看到双端迭代器的定义,我瞬间兴奋了。终于可以一睹迭代器的实现了。我们首先看一下迭代器的定义。

/* * 双端链表迭代器 */typedef struct listIter {    // 当前迭代到的节点    listNode *next;    // 迭代的方向    int direction;} listIter;

图14
然后我们看一下迭代器的实现:

/* Returns a list iterator 'iter'. After the initialization every * call to listNext() will return the next element of the list. * * This function can't fail. *//* * 为给定链表创建一个迭代器, * 之后每次对这个迭代器调用 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);}/* Create an iterator in the list private iterator structure *//* * 将迭代器的方向设置为 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;}/* Return the next element of an iterator. * It's valid to remove the currently returned element using * listDelNode(), but not to remove other elements. * * The function returns a pointer to the next element of the list, * or NULL if there are no more elements, so the classical usage patter * is: * * iter = listGetIterator(list,<direction>); * while ((node = listNext(iter)) != NULL) { *     doSomethingWith(listNodeValue(node)); * } * * *//* * 返回迭代器当前所指向的节点。 * * 删除当前节点是允许的,但不能修改链表里的其他节点。 * * 函数要么返回一个节点,要么返回 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;}

图15
总结:
这样看下来其实迭代器的实现也就是故作神秘,逻辑很简单。
看这个链表实现的代码的收获就是把链表这个数据结构完整的复习了一遍,并且学会了如何给链表设置迭代器了。

0 0
原创粉丝点击