1.前言
本文的内容是基于 redis-7.0.0
的源码,具体的源码可以参阅 redis 7.0.0。本文也不会对源文件 adlist.h/adlist.c
中所有的源码进行解释,只会讲解其中的主干部分,如果有兴趣可以参阅 adlist.h 和 adlist.c。
2.list 定义
在学数据结构这么门课上,我们都学过双向链表。其实 redis 中的 list 基本数据结构就是双向链表,下面 struct list 的定义如下:
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;
struct listNode 的定义如下:
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
struct list 的 head 和 tail 分别指向链表的第一个结点和最后一个结点。
head
指向 list 的第一个结点。tail
指向 list 的最后一个结点。- free 是函数指针,list 中的 free 可以设置为对 listNode value 字段进行释放的函数,当进行释放操作时,可以使用设置的 free 函数进行 value 字段的释放操作。
dup
是函数指针,list 中的 dup 可以设置为 listNode value 字段进行复制的函数。当进行复制时,会使用设置的 dup 函数进行 value 字段的复制操作。- match 是函数指针,list 中的 match 可以设置为 listNode value 字段的比较函数,当进行比较时,会使用设置的 match 函数进行 value 字段的比较操作。
len
字段的值为该 list 中 node 的个数,代表 list 中的元素个数。
其内存结构可由下图表示:
从上图可以看出,list 中间的 node 的 prev 和 next 分别指向其前面的 node 和 后面的 node。第一个 node 的 prev 的值为 NULL,最后一个 node 的 next 的值为 NULL。 一个空的 list 其 head 和 tail 的值均为 NULL。
3.迭代器
list 中定义了 struct listIter 来遍历 list。其定义的代码如下:
typedef struct listIter {
listNode *next;
int direction;
} listIter;
其中 next 表示当前正在访问的元素,而 direction 表示迭代的方向。
/* Directions for iterators */
#define AL_START_HEAD 0
#define AL_START_TAIL 1
direction
的值为 AL_START_HEAD
表示从头开始向后遍历,AL_START_TAIL
表示从尾向前遍历。
4. 函数
4.1 创建 list
listCreate 函数创建一个 node 数量为 0 的 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;
}
新创建的 list,head 和 tail 的值为 NULL,且 len 的值为 0。
4.2 清空 list
listEmpty 函数释放 list 中的 node。代码如下:
/* Remove all the elements from the list without destroying the list itself. */
void listEmpty(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;
}
list->head = list->tail = NULL;
list->len = 0;
}
这时候,需要注意的是,在判断 list->free, 表示是否需要释放 node 中的 value 字段。如果 node->value 有额外的释放内存方法,那么需要调用该方法来释放内存。if (list->free) list->free(current->value);
这一段代码就是判断 list 有没有通过 listSetFreeMethod
来设置 free 函数。
#define listSetFreeMethod(l,m) ((l)->free = (m))
4.3 创建迭代器
listGetIterator 创建一个 list 迭代器。代码如下:
/* 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. */
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;
}
为什么需要迭代器,将遍历 list 操作结构化
,如果我们想从前往后遍历一个 list。那么就需要定义一个 listNode * 指向 list 的 head。
listNode *cur = l->head;
while (cur != NULL) {
do something
cur = cur->next;
}
而现在我们有了 listIter,就可以写出如下代码:
listIter *iter = listGetIterator(l, AL_START_HEAD)
while (node = liestNext(&iter)) != NULL) {
do something
}
listNext 将迭代器当前指向的元素返回,并且指向迭代器下一个元素(如果是向前遍历,则是上一个元素)。
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;
}
4.4 复制 list
listDup 函数复制一个 list,代码如下:
/* Duplicate the whole list. On out of memory NULL is returned.
* On success a copy of the original list is returned.
*
* The 'Dup' method set with listSetDupMethod() function is used
* to copy the node value. Otherwise the same pointer value of
* the original node is used as value of the copied node.
*
* The original list both on success or error is never modified. */
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;
listRewind(orig, &iter);
while((node = listNext(&iter)) != NULL) {
void *value;
//如果list 的 dup 函数不为 NULL,则对 node 中的 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) {
/* Free value if dup succeed but listAddNodeTail failed. */
if (copy->free) copy->free(value);
listRelease(copy);
return NULL;
}
}
return copy;
}
这里需要注意的是:如果 list 的 dup 函数不为 NULL,则对 node 中的 value 字段进行复制函数调用。
4.5
listSearch 函数查找一个 value 字段值为 key 的 node,代码如下:
/* 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. */
listNode *listSearchKey(list *list, void *key)
{
listIter iter;
listNode *node;
listRewind(list, &iter);
while((node = listNext(&iter)) != NULL) {
//如果 list match 字段的值不为 NULL,则需要调用 match 指向的函数对 key 和 node 的 value 进行比较。
if (list->match) {
if (list->match(node->value, key)) {
return node;
}
} else {
if (key == node->value) {
return node;
}
}
}
return NULL;
}
需要注意的是:如果 list match 字段的值不为 NULL,则需要调用 match 指向的函数对 key 和 node 的 value 进行比较。
5.总结
- list 类型是一个双向无循环的链表结构。
- 采用 iter 对 list 中的 node 进行遍历。
- 可以通过设定free,dup 和 match 的值,针对不同类型的 list 中的 node value 字段进行释放,复制和比较操作。