Redis設計與實現讀書筆記——雙鏈表

來源:互聯網
上載者:User
前言首先,貼一下參考連結:http://www.redisbook.com/en/latest/internal-datastruct/adlist.html, 另外真贊文章的作者,一個90後的小夥真不錯,基本功紮實,而且非常樂於助人概述鏈表是Redis的核心資料結構之一,它不僅大量應用在Redis自身內部的實現中,而且它也是Redis的List的結構的底層實現之一這裡分析的是Redis源碼裡adlist.h和adlist.c資料結構Redis的鏈表結構是一種典型的雙端鏈表doubly linked list實現除了一個指向值的void指標外,鏈表中的每個節點都有兩個方向指標,一個指向前驅節點,一個指向後繼節點
/* * 鏈表節點 */typedef struct listNode {    // 前驅節點    struct listNode *prev;    // 後繼節點    struct listNode *next;    // 值    void *value;} listNode;

每個雙端鏈表都被一個list結構封裝起來,list結構帶有兩個指標,一個指向雙端鏈表的表前端節點,另一個指向雙端鏈表的表尾節點,這個特性使得Redis可以很方便執行像RPOP LPUSH這樣的命令:

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

鏈表結構中還有三個函數指標 dup, free 和match,這些指標指向那些用於處理不同類型值的函數至於len屬性,就是鏈表節點數量計數器了以下是雙端鏈表和節點的一個:list結構和listNode結構的APIlist和listNode都有它們自己的一族API,這裡貼出來學習一下redis的源碼(ps:下面的代碼都是我仿照redis改寫能直接編譯啟動並執行代碼)list *listCreate(void)

/** * 建立一個新列表 * * T = O(1)                                                                                                                              */list *listCreate(void){    struct list *list;    // 為列表結構分配記憶體    list = (struct list *)malloc(sizeof(struct list));    if (list == NULL)        return NULL;    // 初始化屬性    list->head = list->tail = NULL;    list->len = 0;    list->dup = NULL;    list->free = NULL;    list->match = NULL;    return list;}

void listRelease(list *list)

/** * 釋放整個列表 * * T = O(N), N為列表長度 */void listRelease(list *list){    unsigned long len;    listNode *current, *next;    current = list->head;    len = list->len;    while (len --) {        next = current->next;        // 如果列表有內建的free方法,那麼先對節點值調用它        if (list->free) list->free(current->value);        // 之後釋放節點        free(current);        current = next;    }    free(list);} 

list *listAddNodeHead(list *list, void *value)

/** * 建立一個包含給定value的節點,並將它加入到列表的表頭 * * T = O(1)                                                                                                                              */list *listAddNodeHead(list *list, void *value){    listNode *node;    node = (listNode *)malloc(sizeof(listNode));    if (node == NULL)        return NULL;    node->value = value;    if (list->len == 0) {        // 第一個節點        list->head = list->tail = node;        node->prev = node->next = NULL;    } else {        // 不是第一個節點        node->prev = NULL;        node->next = list->head;        list->head->prev = node;        list->head = node;    }    list->len ++;    return list;}

list *listAddNodeTail(list *list, void *value)

/** * 建立一個包含給定value的節點,並把它加入到列表的表尾 * * T = O(1) */list *listAddNodeTail(list *list, void *value){    listNode *node;        node = (listNode *)malloc(sizeof(listNode));    if (node == NULL)        return NULL;    if (list->len == 0) {        // 第一個節點        list->head = list->tail = node;        node->prev = node->next = NULL;    } else {        // 不是第一節點        node->prev = list->tail;        node->next = NULL;        list->tail->next = node;        list->tail = node;    }    list->len ++;    return list;}
list *listInsertNode(list *list, listNode *old_node, void *value, int after)
/** * 建立一個包含值value的節點 * 並根據after參數的指示,將新節點插入到old_node的之前或者之後 * * T = O(1) */list *listInsertNode(list *list, listNode *old_node, void *value, int after){listNode *node;node = (listNode *)malloc(sizeof(listNode));if (node == NULL)return NULL;if (after) {// 插入到old_node之後node->prev = old_node;node->next = old_node->next;// 處理表尾節點if (list->tail == old_node) {list->tail = node;}} else {// 插入到old_node之前node->next = old_node;node->prev = old_node->prev;// 處理表前端節點if (list->head == old_node) {list->head = node;}}// 更新前置節點和後繼節點的指標(這個地方很經典,節約代碼)if (node->prev != NULL) {node->prev->next = node;}if (node->next != NULL) {node->next->prev = node;}// 更新列表節點list->len ++;return list;}

void listDelNode(list *list, listNode *node)

/** * 釋放列表中給定的節點 * * T = O(1) */void listDelNode(list *list, listNode *node){// 處理前驅節點指標if (node->prev) {node->prev->next = node->next;} else {list->head = node->next;}// 處理後繼節點if (node->next) {node->next->prev = node->prev;} else {list->tail = node->prev;}// 釋放節點值if (list->free) list->free(node->value);// 釋放節點free(node);// 更新列表節點數目list->len --;}

迭代器其實我對迭代器的概念非常陌生,因為我是純c程式員,不會c++,這裡直接跟著學了!Redis針對list結構實現了一個迭代器,用於對鏈表進行遍曆迭代器的結構定義如下:

/** * 鏈表迭代器 */typedef struct listIter {// 下一節點listNode *next;// 迭代方向int direction;} listIter;

direction決定了迭代器是沿著next指標向後迭代,還是沿著prev指標向前迭代,這個值可以是adlist.h中的AL_START_HEAD常量或AL_START_TAIL常量:

#define AL_START_HEAD 0#define AL_START_TAIL 1

學習一下迭代器的api實現:listIter *listGetIterator(list *list, int direction)

/** * 建立列表list的一個迭代器,迭代方向由參數direction決定 * * 每次對迭代器listNext(),迭代器返回列表的下一個節點 * * T = O(1) */listIter *listGetIterator(list *list, int direction){listIter *iter;iter = (listIter *)malloc(sizeof(listIter));if (iter == NULL)return NULL;// 根據迭代器的方向,將迭代器的指標指向表頭或者表尾if (direction == AL_START_HEAD) {iter->next = list->head;} else {iter->next = list->tail;}// 記錄方向iter->direction = direction;return iter;}

void listRewind(list *list, listIter *li)

/** * 將迭代器iter的迭代指標倒回list的表頭 * * T = O(1) */void listRewind(list *list, listIter *li){li->next = list->head;li->direction = AL_START_HEAD;}

void listRewindTail(list *list, listIter *li)

/** * 將迭代器iter的迭代指標倒回list的表尾 * * T = O(1) */void listRewindTail(list *list, listIter *li){li->next = list->tail;li->direction = AL_START_TAIL;}

listNode *listNext(listIter *iter)

/** * 函數要麼返回當前節點,要麼返回NULL,因此,常見的用法是: * iter = listGetIterator(list, <direction>); * while ((node = listNext(iter)) != NULL) { *     doSomethingWith(listNodeValue(node)); * } * * T = O(1) */listNode *listNext(listIter *iter){listNode *current = iter->next;if (current != NULL) {// 根據迭代方向,選擇節點if (iter->direction == AL_START_HEAD)iter->next = current->next;elseiter->next = current->prev;}return current;}

小結雖然上面的代碼我也都能實現,但是不得不概括redis的代碼規範,寫的真不錯!!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.