redis源碼學習_鏈表
阿新 • • 發佈:2017-09-17
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源碼學習_鏈表