redis 5.0.2 源码阅读——双向链表
redis中双向链表相关的文件为:adlist.h与adlist.c
一、数据结构
redis里定义的双向链表,与普通双向链表大致相同
1.1 单个节点结构
1 /* Node, List, and Iterator are the only data structures used currently. 链表节点*/
2
3 typedef struct listNode {
4 struct listNode *prev;
5 struct listNode *next;
6 void *value;
7 } listNode;
1.2 链表结构
1 /**
2 * 链表
3 * 链表以函数指针的方式,实现了复制、销毁与比较的方法的多态。
4 */
5 typedef struct list {
6 listNode *head;
7 listNode *tail;
8 void *(*dup)(void *ptr);
9 void (*free)(void *ptr); //指定的节点释放函数
10 int (*match)(void *ptr, void *key);
11 unsigned long len;
12 } list;
1.3 迭代器
1 /**
2 * 迭代器
3 * 迭代器中有个成员变量direction,用于表示当前遍历的方向。
4 */
5 typedef struct listIter {
6 listNode *next;
7 int direction;
8 } listIter;
1.4 结构原理
1 /*
2 +-------------------+ +----------------> +--------------+ <-------+
3 |listNode *head |--------+ |listNode *prev|-->NULL |
4 +-------------------+ +--------------+ |
5 |listNode *tail |--------+ |listNode *next|----+ |
6 +-------------------+ | +--------------+ | |
7 |void *(*dup)(...) | | |void *value | | |
8 +-------------------+ | +--------------+ | |
9 |void (*free)(...) | | | |
10 +-------------------+ | | |
11 |int (*match)(...) | | | |
12 +-------------------+ +----------------> +--------------+ <--+ |
13 |unsigned long len | |listNode *prev|---------+
14 +-------------------+ +--------------+
15 |listNode *next|-->NULL
16 +--------------+
17 |void *value |
18 +--------------+
19 */
二、创建
2.1 链表创建
redis中创建一个初始双向链表比较简单,只要分配好内存,并给成员变量赋初值就可以了
1 /* Create a new list. The created list can be freed with
2 * AlFreeList(), but private value of every node need to be freed
3 * by the user before to call AlFreeList().
4 *
5 * On error, NULL is returned. Otherwise the pointer to the new list.
6 * redis中创建一个初始双向链表比较简单,只要分配好内存,并给成员变量赋初值就可以了
7 * redis中提供了头插法、尾插法以及指定位置插入节点三种方式向链表中添加节点,与普通双向链表无异。
8 * */
9 list *listCreate(void)
10 {
11 struct list *list;
12
13 if ((list = zmalloc(sizeof(*list))) == NULL)
14 return NULL;
15 list->head = list->tail = NULL;
16 list->len = 0;
17 list->dup = NULL;
18 list->free = NULL;
19 list->match = NULL;
20 return list;
21 }
2.2 链表插入
redis中提供了头插法、尾插法以及指定位置插入节点三种方式向链表中添加节点,与普通双向链表无异。
2.2.1 头插法
1 /* Add a new node to the list, to head, 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 * */
9 list *listAddNodeHead(list *list, void *value)
10 {
11 listNode *node;
12
13 if ((node = zmalloc(sizeof(*node))) == NULL)
14 return NULL;
15 node->value = value;
16 if (list->len == 0) {
17 list->head = list->tail = node;
18 node->prev = node->next = NULL;
19 } else {
20 node->prev = NULL;
21 node->next = list->head;
22 list->head->prev = node;
23 list->head = node;
24 }
25 list->len++;
26 return list;
27 }
2.2.2 尾插法
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 * */
9 list *listAddNodeTail(list *list, void *value)
10 {
11 listNode *node;
12
13 if ((node = zmalloc(sizeof(*node))) == NULL)
14 return NULL;
15 node->value = value;
16 if (list->len == 0) {
17 list->head = list->tail = node;
18 node->prev = node->next = NULL;
19 } else {
20 node->prev = list->tail;
21 node->next = NULL;
22 list->tail->next = node;
23 list->tail = node;
24 }
25 list->len++;
26 return list;
27 }
1 list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
2 listNode *node;
3
4 if ((node = zmalloc(sizeof(*node))) == NULL)
5 return NULL;
6 node->value = value;
7 //如果为真,插在指定节点的后面,否则插在指定节点的前面
8 if (after) {
9 node->prev = old_node;
10 node->next = old_node->next;
11 if (list->tail == old_node) {
12 list->tail = node;
13 }
14 } else {
15 node->next = old_node;
16 node->prev = old_node->prev;
17 if (list->head == old_node) {
18 list->head = node;
19 }
20 }
21 if (node->prev != NULL) {
22 node->prev->next = node;
23 }
24 if (node->next != NULL) {
25 node->next->prev = node;
26 }
27 list->len++;
28 return list;
29 }
三、销毁
因链表中每个节点的value可能指向堆空间,故不能直接把list结构体free,这样会造成内存泄露。需要先将每个节点的value释放,才可以free结构体。
3.1 删除所有节点
1 /**
2 * Remove all the elements from the list without destroying the list itself.
3 *
4 * 清空所有节点:
5 *
6 * 因链表中每个节点的value可能指向堆空间,故不能直接把list结构体free,这样会造成内存泄露。
7 * 需要先将每个节点的value释放,才可以free结构体
8 */
9 void listEmpty(list *list)
10 {
11 unsigned long len;
12 listNode *current, *next;
13
14 current = list->head;
15 len = list->len;
16 while(len--) {
17 next = current->next;
18 //若指定了销毁的函数,则使用指定的函数进行销毁value
19 if (list->free)
20 list->free(current->value);
21 zfree(current);
22 current = next;
23 }
24 list->head = list->tail = NULL;
25 list->len = 0;
26 }
27
28 /* Free the whole list.
29 *
30 * This function can't fail.
31 *
32 * 销毁链表
33 * */
34 void listRelease(list *list)
35 {
36 listEmpty(list);
37 zfree(list);
38 }
3.2 删除指定节点
同样,redis的链表提供了与普通链表相同的删除单个节点的操作。
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 * 从链表中删除指定节点
7 * */
8 void listDelNode(list *list, listNode *node)
9 {
10 if (node->prev)
11 node->prev->next = node->next;
12 else
13 list->head = node->next;
14 if (node->next)
15 node->next->prev = node->prev;
16 else
17 list->tail = node->prev;、
18
19 //如果该节点指定了自己的释放函数就使用自己的释放函数
20 if (list->free)
21 list->free(node->value);
22 zfree(node);
23
24 //修正链表长度
25 list->len--;
26 }
四、迭代器操作
redis中提供了获取迭代器的接口
4.1 迭代器的获取
得到链表的指定遍历方向的迭代器:
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 * */
8 listIter *listGetIterator(list *list, int direction)
9 {
10 listIter *iter;
11
12 if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
13 //从头开始遍历还是从尾开始遍历
14 if (direction == AL_START_HEAD)
15 iter->next = list->head;
16 else
17 iter->next = list->tail;
18
19 //指定遍历方向
20 iter->direction = direction;
21 return iter;
22 }
4.2 迭代器结构
以AL_START_HEAD为例,生成好的迭代器结构如下:
1 /*
2 +-------------------+ +---> +--------------+ <-------+----+
3 |listNode *head |----+ |listNode *prev|-->NULL | |
4 +-------------------+ +--------------+ | | +--------------+
5 |listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
6 +-------------------+ | +--------------+ | | +--------------+
7 |void *(*dup)(...) | | |void *value | | | |int direction |
8 +-------------------+ | +--------------+ | | +--------------+
9 |void (*free)(...) | | | |
10 +-------------------+ | | |
11 |int (*match)(...) | | | |
12 +-------------------+ +---> +--------------+ <--+ |
13 |unsigned long len | |listNode *prev|---------+
14 +-------------------+ +--------------+
15 |listNode *next|-->NULL
16 +--------------+
17 |void *value |
18 +--------------+
19 */
4.3 迭代器的next方法
1 /* Return the next element of an iterator.
2 * It's valid to remove the currently returned element using
3 * listDelNode(), but not to remove other elements.
4 *
5 * The function returns a pointer to the next element of the list,
6 * or NULL if there are no more elements, so the classical usage patter
7 * is:
8 *
9 * iter = listGetIterator(list,<direction>);
10 * while ((node = listNext(iter)) != NULL) {
11 * doSomethingWith(listNodeValue(node));
12 * }
13 *
14 * 迭代器的next方法
15 * 调用next函数的返回值为调用之前的listNode首地址
16 * */
17 listNode *listNext(listIter *iter)
18 {
19 listNode *current = iter->next;
20
21 if (current != NULL) {
22 if (iter->direction == AL_START_HEAD)
23 iter->next = current->next;
24 else
25 iter->next = current->prev;
26 }
27 return current;
28 }
调用一次之后的结构:
1 /*
2 +-------------------+ +---> +--------------+ <-------+
3 |listNode *head |----+ |listNode *prev|-->NULL |
4 +-------------------+ +--------------+ | +--------------+
5 |listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
6 +-------------------+ | +--------------+ | | | +--------------+
7 |void *(*dup)(...) | | |void *value | | | | |int direction |
8 +-------------------+ | +--------------+ | | | +--------------+
9 |void (*free)(...) | | | | |
10 +-------------------+ | | | |
11 |int (*match)(...) | | | | |
12 +-------------------+ +---> +--------------+ <--+----|----+
13 |unsigned long len | |listNode *prev|---------+
14 +-------------------+ +--------------+
15 |listNode *next|-->NULL
16 +--------------+
17 |void *value |
18 +--------------+
19 */
再次调用:
1 /*
2 +-------------------+ +---> +--------------+ <-------+
3 |listNode *head |----+ |listNode *prev|-->NULL |
4 +-------------------+ +--------------+ | +--------------+
5 |listNode *tail |----+ |listNode *next|----+ | +--|listNode *next|
6 +-------------------+ | +--------------+ | | | +--------------+
7 |void *(*dup)(...) | | |void *value | | | | |int direction |
8 +-------------------+ | +--------------+ | | | +--------------+
9 |void (*free)(...) | | | | |
10 +-------------------+ | | | |
11 |int (*match)(...) | | | | |
12 +-------------------+ +---> +--------------+ <--+ | +-->NULL
13 |unsigned long len | |listNode *prev|---------+
14 +-------------------+ +--------------+
15 |listNode *next|-->NULL
16 +--------------+
17 |void *value |
18 +--------------+
19 */
4.4 迭代器获取
4.4.1 头部迭代器
得到链表的从头开始遍历的迭代器:
1 /* Create an iterator in the list private iterator structure 返回从头开始遍历的迭代器*/
2 void listRewind(list *list, listIter *li) {
3 li->next = list->head;
4 li->direction = AL_START_HEAD;
5 }
4.4.2 尾部迭代器
得到链表的从尾开始遍历的迭代器:
1 //返回从尾开始遍历的迭代器
2 void listRewindTail(list *list, listIter *li) {
3 li->next = list->tail;
4 li->direction = AL_START_TAIL;
5 }
4.5 释放迭代器
1 /* Release the iterator memory 释放迭代器*/
2 void listReleaseIterator(listIter *iter) {
3 zfree(iter);
4 }
五、其它操作
redis的双向链表还提供了其它操作。其中,查找指定的key与复制整个list依赖于迭代器的使用,并使用到自定义的比较/复制方法。
除此之外,还提供了类似随机读取的方式,其内部实现为遍历,且“越界”时返回NULL。同时,它支持index为负数,表示从尾开始。类似旋转的操作,把尾节点移至原头节点之前,成为新的头节点。当然,还有拼接两个链表的操作。
5.1 链表的拷贝
拷贝链表:
1 /**
2 * Duplicate the whole list. On out of memory NULL is returned.
3 * 复制整个列表。 内存不足时返回 NULL。
4 * On success a copy of the original list is returned.
5 * 成功后,将返回原始列表的副本。
6 *
7 * The 'Dup' method set with listSetDupMethod() function is used
8 * to copy the node value. Otherwise the same pointer value of
9 * the original node is used as value of the copied node.
10 * 使用 listSetDupMethod() 函数设置的 'Dup' 方法用于复制节点值。 否则,原始节点的相同指针值将用作复制节点的值。
11 * 也就是直接将原始链表中节点的值拷贝一份,然后重新创建一个链表节点插入新链表的尾部
12 *
13 * The original list both on success or error is never modified.
14 * 不论拷贝结果是否成功,原始链表都不会被修改,执行的是深拷贝
15 * */
16 list *listDup(list *orig)
17 {
18 list *copy;
19 listIter iter;
20 listNode *node;
21
22 if ((copy = listCreate()) == NULL)
23 return NULL;
24 copy->dup = orig->dup;
25 copy->free = orig->free;
26 copy->match = orig->match;
27 listRewind(orig, &iter);
28 while((node = listNext(&iter)) != NULL) {
29 void *value;
30
31 if (copy->dup) {
32 value = copy->dup(node->value);
33 if (value == NULL) {
34 listRelease(copy);
35 return NULL;
36 }
37 } else
38 value = node->value;
39 if (listAddNodeTail(copy, value) == NULL) {
40 listRelease(copy);
41 return NULL;
42 }
43 }
44 return copy;
45 }
5.2 获取链表节点
5.2.1 根据节点值(key)得到链表节点
1 /* Search the list for a node matching a given key.
2 * The match is performed using the 'match' method
3 * set with listSetMatchMethod(). If no 'match' method
4 * is set, the 'value' pointer of every node is directly
5 * compared with the 'key' pointer.
6 *
7 * On success the first matching node pointer is returned
8 * (search starts from head). If no matching node exists
9 * NULL is returned.
10 *
11 * 返回与指定值key相匹配的从头开始的第一个链表节点
12 * */
13 listNode *listSearchKey(list *list, void *key)
14 {
15 listIter iter;
16 listNode *node;
17
18 listRewind(list, &iter);
19 while((node = listNext(&iter)) != NULL) {
20 if (list->match) {
21 if (list->match(node->value, key)) {
22 return node;
23 }
24 } else {
25 if (key == node->value) {
26 return node;
27 }
28 }
29 }
30 return NULL;
31 }
5.2.2 根据节点位置得到链表节点
1 /* Return the element at the specified zero-based index
2 * where 0 is the head, 1 is the element next to head
3 * and so on. Negative integers are used in order to count
4 * from the tail, -1 is the last element, -2 the penultimate
5 * and so on. If the index is out of range NULL is returned.
6 * 返回指定的索引的节点,索引值index可以为负数,-1代表尾节点
7 * */
8 listNode *listIndex(list *list, long index) {
9 listNode *n;
10
11 if (index < 0) {
12 index = (-index)-1;
13 n = list->tail;
14 while(index-- && n) n = n->prev;
15 } else {
16 n = list->head;
17 while(index-- && n) n = n->next;
18 }
19 return n;
20 }
5.3 将尾节点调整为头节点
1 /**
2 * Rotate the list removing the tail node and inserting it to the head.
3 * 将尾节点调整尾头节点
4 */
5 void listRotate(list *list) {
6 listNode *tail = list->tail;
7
8 //如果链表的长度小于等1
9 if (listLength(list) <= 1) return;
10
11 /* Detach current tail */
12 list->tail = tail->prev;
13 list->tail->next = NULL;
14 /* Move it as head */
15 list->head->prev = tail;
16 tail->prev = NULL;
17 tail->next = list->head;
18 list->head = tail;
19 }
5.4 链表的合并
合并两个链表:
1 /** Add all the elements of the list 'o' at the end of the
2 * list 'l'. The list 'other' remains empty but otherwise valid.
3 * 将链表o合并到链表l的后面
4 */
5 void listJoin(list *l, list *o) {
6 if (o->head)
7 o->head->prev = l->tail;
8
9 if (l->tail)
10 l->tail->next = o->head;
11 else
12 l->head = o->head;
13
14 if (o->tail) l->tail = o->tail;
15 l->len += o->len;
16
17 /* Setup other as an empty list. */
18 o->head = o->tail = NULL;
19 o->len = 0;
20 }
参考文章
https://www.cnblogs.com/chinxi/p/12233306.html
本文来自博客园,作者:Mr-xxx,转载请注明原文链接:https://www.cnblogs.com/MrLiuZF/p/14967608.html