1. 程式人生 > >redis源碼學習_鏈表

redis源碼學習_鏈表

str release ota art 鏈表結構 不能 clas 插入 選擇

redis的鏈表是雙向鏈表,該鏈表不帶頭結點,具體如下:

技術分享

主要總結一下adlist.c和adlist.h裏面的關鍵結構體和函數。

鏈表節點結構如下:

 1 /*
 2  * 雙端鏈表節點
 3  */
 4 typedef struct listNode {
 5 
 6     // 前置節點
 7     struct listNode *prev; //如果是list的頭結點,則prev指向NULL
 8 
 9     // 後置節點
10     struct listNode *next;//如果是list尾部結點,則next指向NULL
11 
12     // 節點的值
13     void *value;
14 15 } listNode;

鏈表結構如下:

 1 /*
 2  * 雙端鏈表結構
 3  */
 4 typedef struct list {
 5 
 6     // 表頭節點
 7     listNode *head;
 8 
 9     // 表尾節點
10     listNode *tail;
11 
12     // 節點值復制函數
13     void *(*dup)(void *ptr);
14 
15     // 節點值釋放函數
16     void (*free)(void *ptr);
17 
18     // 節點值對比函數
19     int (*match)(void
*ptr, void *key); 20 21 // 鏈表所包含的節點數量,有了這個我們獲得鏈表長度的時間復雜度就是O(1)了 22 unsigned long len; 23 24 } list;

鏈表叠代器的結構如下:

 1 /*
 2  * 雙端鏈表叠代器
 3  */
 4 typedef struct listIter {
 5 
 6     // 當前叠代到的節點
 7     listNode *next;
 8 
 9     // 叠代的方向
10     int direction; //取值AL_START_HEAD等
11 
12 } listIter;

裏面涉及的函數中,增、刪的比較簡單,就是結構裏面沒有帶頭結點,所以需要單獨判斷一下頭結點的特殊情況。另外對於尾節點的操作也需要考慮一下特殊情況。

listAddNodeHead:將一個包含給定值的新節點添加到給定鏈表的表頭

 1 /* Add a new node to the list, to head, contaning 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  * 將一個包含有給定值指針 value 的新節點添加到鏈表的表頭
 9  *
10  * 如果為新節點分配內存出錯,那麽不執行任何動作,僅返回 NULL
11  *
12  * 如果執行成功,返回傳入的鏈表指針
13  *
14  * T = O(1)
15  */
16 list *listAddNodeHead(list *list, void *value)
17 {
18     listNode *node;
19 
20     // 為節點分配內存
21     if ((node = zmalloc(sizeof(*node))) == NULL)
22         return NULL;
23 
24     // 保存值指針
25     node->value = value;
26 
27     // 添加節點到空鏈表
28     if (list->len == 0) {
29         list->head = list->tail = node;
30         node->prev = node->next = NULL;
31     // 添加節點到非空鏈表
32     } else {
33         node->prev = NULL;
34         node->next = list->head;
35         list->head->prev = node;
36         list->head = node;
37     }
38 
39     // 更新鏈表節點數
40     list->len++;
41 
42     return list;
43 }

listAddNodeTail:將一個包含給定值的新節點添加到給定鏈表的表尾

 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  * 將一個包含有給定值指針 value 的新節點添加到鏈表的表尾
 9  *
10  * 如果為新節點分配內存出錯,那麽不執行任何動作,僅返回 NULL
11  *
12  * 如果執行成功,返回傳入的鏈表指針
13  *
14  * T = O(1)
15  */
16 list *listAddNodeTail(list *list, void *value)
17 {
18     listNode *node;
19 
20     // 為新節點分配內存
21     if ((node = zmalloc(sizeof(*node))) == NULL)
22         return NULL;
23 
24     // 保存值指針
25     node->value = value;
26 
27     // 目標鏈表為空
28     if (list->len == 0) {
29         list->head = list->tail = node;
30         node->prev = node->next = NULL;
31     // 目標鏈表非空
32     } else {
33         node->prev = list->tail;
34         node->next = NULL;
35         list->tail->next = node;
36         list->tail = node;
37     }
38 
39     // 更新鏈表節點數
40     list->len++;
41 
42     return list;
43 }

listInsertNode:將一個包含給定值的新節點添加到給定節點的之前或者之後

 1 /*
 2  * 創建一個包含值 value 的新節點,並將它插入到 old_node 的之前或之後
 3  *
 4  * 如果 after 為 0 ,將新節點插入到 old_node 之前。
 5  * 如果 after 為 1 ,將新節點插入到 old_node 之後。
 6  *
 7  * T = O(1)
 8  */
 9 list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
10     listNode *node;
11 
12     // 創建新節點
13     if ((node = zmalloc(sizeof(*node))) == NULL)
14         return NULL;
15 
16     // 保存值
17     node->value = value;
18 
19     // 將新節點添加到給定節點之後
20     if (after) {
21         node->prev = old_node;
22         node->next = old_node->next;
23         // 給定節點是原表尾節點
24         if (list->tail == old_node) {
25             list->tail = node;
26         }
27     // 將新節點添加到給定節點之前
28     } else {
29         node->next = old_node;
30         node->prev = old_node->prev;
31         // 給定節點是原表頭節點
32         if (list->head == old_node) {
33             list->head = node;
34         }
35     }
36 
37     // 更新新節點的前置指針
38     if (node->prev != NULL) {
39         node->prev->next = node;
40     }
41     // 更新新節點的後置指針
42     if (node->next != NULL) {
43         node->next->prev = node;
44     }
45 
46     // 更新鏈表節點數
47     list->len++;
48 
49     return list;
50 }

listDelNode:從鏈表中刪除給定節點

 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  * 從鏈表 list 中刪除給定節點 node 
 7  * 
 8  * 對節點私有值(private value of the node)的釋放工作由調用者進行。
 9  *
10  * T = O(1)
11  */
12 void listDelNode(list *list, listNode *node)
13 {
14     // 調整前置節點的指針
15     if (node->prev)
16         node->prev->next = node->next;
17     else
18         list->head = node->next;
19 
20     // 調整後置節點的指針
21     if (node->next)
22         node->next->prev = node->prev;
23     else
24         list->tail = node->prev;
25 
26     // 釋放值
27     if (list->free) list->free(node->value);
28 
29     // 釋放節點
30     zfree(node);
31 
32     // 鏈表數減一
33     list->len--;
34 }

listGetIterator:創建一個鏈表叠代器,並根據傳入的direction來返回鏈表的頭或尾節點

 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  * 之後每次對這個叠代器調用 listNext 都返回被叠代到的鏈表節點
 8  *
 9  * direction 參數決定了叠代器的叠代方向:
10  *  AL_START_HEAD :從表頭向表尾叠代
11  *  AL_START_TAIL :從表尾想表頭叠代
12  *
13  * T = O(1)
14  */  //獲取列表list的首部階段或者尾部結點
15 listIter *listGetIterator(list *list, int direction)
16 {
17     // 為叠代器分配內存
18     listIter *iter;
19     if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
20 
21     // 根據叠代方向,設置叠代器的起始節點
22     if (direction == AL_START_HEAD)
23         iter->next = list->head;
24     else
25         iter->next = list->tail;
26 
27     // 記錄叠代方向
28     iter->direction = direction;
29 
30     return iter;
31 }

listNext:拿到下一個叠代器節點

 1 /*
 2  * 返回叠代器當前所指向的節點。
 3  *
 4  * 刪除當前節點是允許的,但不能修改鏈表裏的其他節點。
 5  *
 6  * 函數要麽返回一個節點,要麽返回 NULL ,常見的用法是:
 7  *
 8  * iter = listGetIterator(list,<direction>);
 9  * while ((node = listNext(iter)) != NULL) {
10  *     doSomethingWith(listNodeValue(node));
11  * }
12  *
13  * T = O(1)
14  */
15 listNode *listNext(listIter *iter)
16 {
17     listNode *current = iter->next;
18 
19     if (current != NULL) {
20         // 根據方向選擇下一個節點
21         if (iter->direction == AL_START_HEAD)
22             // 保存下一個節點,防止當前節點被刪除而造成指針丟失
23             iter->next = current->next;
24         else
25             // 保存下一個節點,防止當前節點被刪除而造成指針丟失
26             iter->next = current->prev;
27     }
28 
29     return current;
30 }

前面說了那麽多操作,其實就是為了把它們組合起來使用,比如下面這個函數,就是上面的綜合體。

listDup:復制鏈表

 1 /* Duplicate the whole list. On out of memory NULL is returned.
 2  * On success a copy of the original list is returned.
 3  *
 4  * The ‘Dup‘ method set with listSetDupMethod() function is used
 5  * to copy the node value. Otherwise the same pointer value of
 6  * the original node is used as value of the copied node.
 7  *
 8  * The original list both on success or error is never modified. */
 9 /*
10  * 復制整個鏈表。
11  *
12  * 復制成功返回輸入鏈表的副本,
13  * 如果因為內存不足而造成復制失敗,返回 NULL 。
14  *
15  * 如果鏈表有設置值復制函數 dup ,那麽對值的復制將使用復制函數進行,
16  * 否則,新節點將和舊節點共享同一個指針。
17  *
18  * 無論復制是成功還是失敗,輸入節點都不會修改。
19  *
20  * T = O(N)
21  */
22 list *listDup(list *orig)
23 {
24     list *copy;
25     listIter *iter;
26     listNode *node;
27 
28     // 創建新鏈表
29     if ((copy = listCreate()) == NULL)
30         return NULL;
31 
32     // 設置節點值處理函數
33     copy->dup = orig->dup;
34     copy->free = orig->free;
35     copy->match = orig->match;
36 
37     // 叠代整個輸入鏈表
38     iter = listGetIterator(orig, AL_START_HEAD);
39     while((node = listNext(iter)) != NULL) {
40         void *value;
41 
42         // 復制節點值到新節點
43         if (copy->dup) {
44             value = copy->dup(node->value);
45             if (value == NULL) {
46                 listRelease(copy);
47                 listReleaseIterator(iter);
48                 return NULL;
49             }
50         } else
51             value = node->value;
52 
53         // 將節點添加到鏈表
54         if (listAddNodeTail(copy, value) == NULL) {
55             listRelease(copy);
56             listReleaseIterator(iter);
57             return NULL;
58         }
59     }
60 
61     // 釋放叠代器
62     listReleaseIterator(iter);
63 
64     // 返回副本
65     return copy;
66 }

最後再提一個旋轉函數listRotate,也沒有什麽需要過多解釋的。

 1 /* Rotate the list removing the tail node and inserting it to the head. */
 2 /*
 3  * 取出鏈表的表尾節點,並將它移動到表頭,成為新的表頭節點。
 4  *
 5  * T = O(1)
 6  */
 7 void listRotate(list *list) {
 8     listNode *tail = list->tail;
 9 
10     if (listLength(list) <= 1) return;
11 
12     /* Detach current tail */
13     // 取出表尾節點
14     list->tail = tail->prev;
15     list->tail->next = NULL;
16 
17     /* Move it as head */
18     // 插入到表頭
19     list->head->prev = tail;
20     tail->prev = NULL;
21     tail->next = list->head;
22     list->head = tail;
23 }

總體來說,鏈表的數據結構是屬於比較簡單的。

redis源碼學習_鏈表