Redis學習筆記——ListNode

yoop發表於2017-03-31

Redis定義了雙向連結串列,用來儲存列表鍵的值,還有其他的我不知的。
雙向連結串列的節點定義為:

typedef struct listNode {

    // 前置節點
    struct listNode *prev;

    // 後置節點
    struct listNode *next;

    // 節點的值
    void *value;

} listNode;

這裡是把節點的值作為了一個成員變數,Linux核心裡面節點的定義是這樣的

struct list_head{
    struct list_head *next;
    struct list_head *prev;
}

這樣可以沒有不用處理節點所帶資料的型別,但是要通過offsetof/container_of這類來處理。不過這是題外話了。
除此之外,Redis還定義了連結串列結構,如下:

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;

這樣定義的好處有:

  1. 快速獲取連結串列的頭尾

  2. 快速查詢連結串列的節點數,像動態字串sdshr裡的len那樣,不用遍歷全部字元就可以獲取字串的長度,這裡也類似。

它裡面還定義了連結串列的迭代器,

typedef struct listIter {

    // 當前迭代到的節點
    listNode *next;

    // 迭代的方向
    int direction;

} listIter;

裡面迭代的方向為

// 從表頭向表尾進行迭代
#define AL_START_HEAD 0
// 從表尾到表頭進行迭代
#define AL_START_TAIL 1

迭代器可以通過以下幾個介面來使用(不完全列出來)

listIter *listGetIterator(list *list, int direction) //根據迭代方向給連結串列建立迭代器
listNode *listNext(listIter *iter) //返回迭代器當前指向的節點,並且把迭代器根據方向向前移動一下

假如我要遍歷整個連結串列,我可以這樣做

iter = listGetIterator(list, AL_START_HEAD);
while ((node = listNext(iter)) != NULL) {
    doSomethingWith(node);
}

寫起來是比較簡單易懂的。
裡面還有個反轉節點的函式

void listRotate(list *list) {
    listNode *tail = list->tail;

    if (listLength(list) <= 1) return;

    /* Detach current tail */
    // 取出表尾節點
    list->tail = tail->prev;
    list->tail->next = NULL;

    /* Move it as head */
    // 插入到表頭
    list->head->prev = tail;
    tail->prev = NULL;
    tail->next = list->head;
    list->head = tail;
}

這個其實是翻轉節點,不是倒序。是不是跟以前做過的演算法題很像。有沒有想起手搖演算法。

最後貼上《Redis設計與實現》裡的總結

連結串列被廣泛用於實現 Redis 的各種功能, 比如列表鍵, 釋出與訂閱, 慢查詢, 監視器, 等等。
每個連結串列節點由一個 listNode 結構來表示, 每個節點都有一個指向前置節點和後置節點的指標, 所以 Redis 的連結串列實現是雙端連結串列。
每個連結串列使用一個 list 結構來表示, 這個結構帶有表頭節點指標、表尾節點指標、以及連結串列長度等資訊。
因為連結串列表頭節點的前置節點和表尾節點的後置節點都指向 NULL , 所以 Redis 的連結串列實現是無環連結串列。
通過為連結串列設定不同的型別特定函式, Redis 的連結串列可以用於儲存各種不同型別的值。

參考:
1.《Redis設計與實現》
2.https://github.com/huangz1990…

相關文章