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

鏈表

Redis中是自己實現的鏈表。鏈表被廣泛用於Redis的各種功能,比如列表鍵、發佈於訂閱、慢查詢、監視器等。

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

特點

來之《Redis設計與實現》

  • 每個鏈表結點由一個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中的節點node

    	/* 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設計與實現》

函數作用時間複雜度
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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章