Redis定义了双向链表,用来存储列表键的值,还有其余的我不知的。
双向链表的节点定义为:node
typedef struct listNode { // 前置节点 struct listNode *prev; // 后置节点 struct listNode *next; // 节点的值 void *value; } listNode;
这里是把节点的值做为了一个成员变量,Linux内核里面节点的定义是这样的git
struct list_head{ struct list_head *next; struct list_head *prev; }
这样能够没有不用处理节点所带数据的类型,可是要经过offsetof/container_of这类来处理。不过这是题外话了。
除此以外,Redis还定义了链表结构,以下:github
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;
这样定义的好处有:redis
快速获取链表的头尾算法
快速查询链表的节点数,像动态字符串sdshr里的len那样,不用遍历所有字符就能够获取字符串的长度,这里也相似。函数
它里面还定义了链表的迭代器,设计
typedef struct listIter { // 当前迭代到的节点 listNode *next; // 迭代的方向 int direction; } listIter;
里面迭代的方向为指针
// 从表头向表尾进行迭代 #define AL_START_HEAD 0 // 从表尾到表头进行迭代 #define AL_START_TAIL 1
迭代器能够经过如下几个接口来使用(不彻底列出来)code
listIter *listGetIterator(list *list, int direction) //根据迭代方向给链表建立迭代器 listNode *listNext(listIter *iter) //返回迭代器当前指向的节点,而且把迭代器根据方向向前移动一下
假如我要遍历整个链表,我能够这样作接口
iter = listGetIterator(list, AL_START_HEAD); while ((node = listNext(iter)) != NULL) { doSomethingWith(node); }
写起来是比较简单易懂的。
里面还有个反转节点的函数
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; }
这个实际上是翻转节点,不是倒序。是否是跟之前作过的算法题很像。有没有想起手摇算法。
最后贴上《Redis设计与实现》里的总结
链表被普遍用于实现 Redis 的各类功能, 好比列表键, 发布与订阅, 慢查询, 监视器, 等等。
每一个链表节点由一个 listNode 结构来表示, 每一个节点都有一个指向前置节点和后置节点的指针, 因此 Redis 的链表实现是双端链表。
每一个链表使用一个 list 结构来表示, 这个结构带有表头节点指针、表尾节点指针、以及链表长度等信息。
由于链表表头节点的前置节点和表尾节点的后置节点都指向 NULL , 因此 Redis 的链表实现是无环链表。
经过为链表设置不一样的类型特定函数, Redis 的链表能够用于保存各类不一样类型的值。
参考:
1.《Redis设计与实现》
2.https://github.com/huangz1990...