• 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

  • 相关阅读:
    输入一个nxn矩阵各元素的值,球出两条对角线元素之和
    打印杨辉三角
    编写一个函数,实现两个字符串的连接功能
    字符串置换。将字符串s中的出现的字符s1用字符s2置换
    有一行文字,要求删去其中某个字符
    自定义函数delstr()的功能是删去字符串s1中所有的"*"
    用微软的kestrel在Linux上利用Apache架设Asp.Net Core环境
    2012年8月14日 星期二 equals()方法 (冲突备份)
    jquery 操作DOM 案例
    FileUpload 控件上传图片和文件
  • 原文地址:https://www.cnblogs.com/MrLiuZF/p/14967608.html
Copyright © 2020-2023  润新知