Redis源碼閱讀筆記-鏈表結構

鏈表

Redis中是本身實現的鏈表。鏈表被普遍用於Redis的各類功能,好比列表鍵、發佈於訂閱、慢查詢、監視器等。node

列表鍵的底層實現之一就是鏈表(Redis3.2 以前,在Redis3.2 後被換成了快速列表quicklist)。當一個列表建包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字符串時,Reids會使用鏈表做爲列表鍵的底層實現。(《Redis設計與實現》)函數

特色

來之《Redis設計與實現》ui

  • 每一個鏈表結點由一個listNode結構來表示,每一個結點都由一個指向前置結點和後置結點的指針,全部Redis的鏈表實現是雙端鏈表。
  • 每一個鏈表使用一個list結構來表示,這個結構帶有表頭結點指針、表位結點指針、已經鏈表長度等信息。
  • 由於鏈表表頭結點的前置節點和表尾結點的後置節點都指向NULL,因此Redis的鏈表實現是無環鏈表。
  • 經過爲鏈表設置不一樣的類型特定函數,Redis的鏈表能夠用於保存各類不一樣類型的值。

代碼結構

// adlist.h

// 鏈表結點
typedef struct listNode {
    struct listNode *prev; // 前置節點
    struct listNode *next; // 後置節點
    void *value; // 節點值
} listNode;

// 鏈表結構
typedef struct list {
    listNode *head; // 表頭節點
    listNode *tail; // 表尾節點
    void *(*dup)(void *ptr); // 節點值複製函數指針
    void (*free)(void *ptr); // 節點值釋放函數指針
    int (*match)(void *ptr, void *key); // 節點值對比函數指針
    unsigned long len; // 鏈表所包含的節點數量
} list;

// 鏈表的迭代器
typedef struct listIter {
    listNode *next; // 下一個節點
    int direction; // 迭代方向
} listIter;
  • dup函數用於複製鏈表節點所保存的值;
  • free函數用於釋放鏈表節點所保存的值;
  • match函數用於對比鏈表節點所保存的值和另外一個輸入值是否相等;

部分代碼解析

  • list *listAddNodeHead(list *list, void *value) 向list中添加鏈表頭:設計

    /* Add a new node to the list, to head, containing the specified 'value'
    	 * pointer as value.
    	 *
    	 * On error, NULL is returned and no operation is performed (i.e. the
    	 * list remains unaltered).
    	 * On success the 'list' pointer you pass to the function is returned. */
    	list *listAddNodeHead(list *list, void *value)
    	{
    	    listNode *node;
    
    	    // 爲鏈表節點分配內存
    	    if ((node = zmalloc(sizeof(*node))) == NULL)
    	        return NULL;
    	    // 給鏈表節點的value賦值
    	    node->value = value;
    
    	    if (list->len == 0) {
    	        // 若是list原本沒有節點,則將表頭節點和表尾節點都設置node
    	        list->head = list->tail = node;
    	        // 且node的前置和後置節點都是NULL
    	        node->prev = node->next = NULL;
    	    } else {
    	        // 若是list原本已經存在節點
    	        // 則將node的前置節點設爲NULL
    	        node->prev = NULL;
    	        // 而後將node的後置節點指向list本來的表頭節點
    	        node->next = list->head;
    	        // 將list本來的表頭節點的前置節點指向node
    	        list->head->prev = node;
    	        // 將node設爲list的新表頭節點
    	        list->head = node;
    	    }
    	    // list的長度加1
    	    list->len++;
    	    return list;
    	}
  • list *listInsertNode(list *list, listNode *old_node, void *value, int after)向list中插入節點value,插入位置是old_nodeafter表示在old_node前仍是後:指針

    list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    	    listNode *node;
    
    	    // 給要插入的節點node分配內存
    	    if ((node = zmalloc(sizeof(*node))) == NULL)
    	        return NULL;
    	    // node的value賦值
    	    node->value = value;
    
    	    if (after) {
    	        // 插入到old_node後
    	        node->prev = old_node;
    	        node->next = old_node->next;
    	        if (list->tail == old_node) {
    	            // 若是old_node本來是表尾節點,則從新將表尾節點指向node
    	            list->tail = node;
    	        }
    	    } else {
    	        // 插入到old_node前
    	        node->next = old_node;
    	        node->prev = old_node->prev;
    	        if (list->head == old_node) {
    	            // 若是old_node本來是表頭節點,則從新將表尾節點指向node
    	            list->head = node;
    	        }
    	    }
    	    if (node->prev != NULL) {
    	        //若是node的前置節點不爲NULL,則將node的前置節點的後置節點指向node
    	        node->prev->next = node;
    	    }
    	    if (node->next != NULL) {
    	        //若是node的後置節點不爲NULL,則將node的後置節點的前置節點指向node
    	        node->next->prev = node;
    	    }
    	    // list的長度加1
    	    list->len++;
    	    return list;
    	}
  • void listDelNode(list *list, listNode *node)刪除list中的節點nodecode

    /* Remove the specified node from the specified list.
    	 * It's up to the caller to free the private value of the node.
    	 *
    	 * This function can't fail. */
    	void listDelNode(list *list, listNode *node)
    	{
    	    if (node->prev)
    	        // 若是node有前置節點
    	        // 則將 node前置節點的後置節點 指向 node的後置節點
    	        node->prev->next = node->next;
    	    else
    	        // 若是node沒有前置節點,則說明node是表頭節點
    	        // 將list的表頭節點指向 node的後置節點
    	        list->head = node->next;
    	    if (node->next)
    	        // 若是node有後置節點
    	        // 則將 node的後置節點的前置節點 指向 node的前置節點
    	        node->next->prev = node->prev;
    	    else
    	        // 若是node沒有後置節點,則說明node是表尾節點
    	        // 將list的表尾節點指向 node的前置節點
    	        list->tail = node->prev;
    
    	    // 若是list有free函數,則調用free函數釋放node中的value
    	    if (list->free) list->free(node->value);
    	    // 釋放node的內存
    	    zfree(node);
    	    // list長度減1
    	    list->len--;
    	}

鏈表API

參考之《Redis設計與實現》orm

函數 做用 時間複雜度
list *listCreate(void) 建立一個不包含任何節點的新鏈表 O(1)
void listRelease(list *list) 釋放整個鏈表 O(N)
void listEmpty(list *list) 將整個鏈表的節點狀況,不釋放鏈表自己 O(N)
list *listAddNodeHead(list *list, void *value) 將一個包含給定值的新節點添加爲鏈表的表頭 O(1)
list *listAddNodeTail(list *list, void *value) 將一個包含給定值的新節點添加爲鏈表的表尾 O(1)
list *listInsertNode(list *list, listNode *old_node, void *value, int after) 將一個包含給定值的新節點添加到鏈表給定節點的前或者後 O(1)
void listDelNode(list *list, listNode *node) 刪除鏈表中的給定節點 O(1)
listIter *listGetIterator(list *list, int direction) 獲取給定鏈表的迭代器 O(1)
listNode *listNext(listIter *iter) 獲取鏈表迭代器的下一個節點 O(1)
void listReleaseIterator(listIter *iter) 釋放鏈表迭代器 O(1)
list *listDup(list *orig) 複製一個給定鏈表的副本 O(N)
listNode *listSearchKey(list *list, void *key) 在給定鏈表中查找並返回鏈表中包含給定值key的節點 O(N)
listNode *listIndex(list *list, long index) 返回鏈表中給定索引index上的節點 O(N)
void listRewind(list *list, listIter *li) 將給定迭代器li指定給定的鏈表list,迭代器li指向的是鏈表list的表頭結點 O(1)
void listRewindTail(list *list, listIter *li) 將給定迭代器li指定給定的鏈表list,但迭代器li指向的是鏈表list的表尾結點 O(1)
void listRotate(list *list) 將鏈表的尾結點彈出,而後將被彈出的節點插入到鏈表的表頭,成爲新的表頭節點 O(1)
void listJoin(list *l, list *o) 將鏈表o的節點追加到鏈表l的表尾後,而後將o設置爲空(不是NULL,而是沒有節點) O(1)
相關文章
相關標籤/搜索