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

posted @ 2021-07-03 21:28  Mr-xxx  阅读(40)  评论(0编辑  收藏  举报