1. 程式人生 > >Redis5.0原始碼解析(二)----------連結串列

Redis5.0原始碼解析(二)----------連結串列

基於Redis5.0

連結串列提供了高效的節點重排能力, 以及順序性的節點訪問方式, 並且可以通過增刪節點來靈活地調整連結串列的長度

每個連結串列節點使用一個 adlist.h/listNode 結構來表示:

//adlist.h  - A generic doubly linked list implementation

/* Node, List, and Iterator are the only data structures used currently. */

typedef struct listNode {
    struct listNode *
prev; struct listNode *next; void *value; } listNode;

在這裡插入圖片描述

雖然僅僅使用多個 listNode 結構就可以組成連結串列, 但使用 adlist.h/list 來持有連結串列的話, 操作起來會更方便:

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;
  • dup 函式用於複製連結串列節點所儲存的值;
  • free 函式用於釋放連結串列節點所儲存的值;
  • match 函式則用於對比連結串列節點所儲存的值和另一個輸入值是否相等。
    在這裡插入圖片描述

list的迭代器Iterator,用來方便快速遍歷list

typedef struct listIter {
    listNode *next;
    //遍歷方向
    int direction;
} listIter;

/* Directions for iterators */
#define AL_START_HEAD 0 #define AL_START_TAIL 1

與Java集合中的Iterator概念類似

Redis 的連結串列實現的特性可以總結如下:

  • 雙端: 連結串列節點帶有 prevnext 指標, 獲取某個節點的前置節點和後置節點的複雜度都是 O(1) 。
  • 無環: 表頭節點的 prev 指標和表尾節點的 next 指標都指向 NULL , 對連結串列的訪問以 NULL 為終點。
  • 帶表頭指標和表尾指標: 通過 list 結構的 head 指標和 tail 指標, 程式獲取連結串列的表頭節點和表尾節點的複雜度為 O(1) 。
  • 帶連結串列長度計數器: 程式使用 list 結構的 len 屬性來對 list 持有的連結串列節點進行計數, 程式獲取連結串列中節點數量的複雜度為 O(1) 。
  • 多型: 連結串列節點使用 void* 指標來儲存節點值, 並且可以通過 list 結構的 dupfreematch 三個屬性為節點值設定型別特定函式, 所以連結串列可以用於儲存各種不同型別的值。

更方便的巨集定義:

/* Functions implemented as macros */
#define listLength(l) ((l)->len)
#define listFirst(l) ((l)->head)
#define listLast(l) ((l)->tail)
#define listPrevNode(n) ((n)->prev)
#define listNextNode(n) ((n)->next)
#define listNodeValue(n) ((n)->value)

#define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m))

#define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)
連結串列API:

建立一個新連結串列:

/* Create a new list. The created list can be freed with
 * AlFreeList(), but private value of every node need to be freed
 * by the user before to call AlFreeList().
 *
 * On error, NULL is returned. Otherwise the pointer to the new list. */
list *listCreate(void)
{
    struct list *list;

    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;
    return list;
}

移除連結串列裡的所有元素:

/* Remove all the elements from the list without destroying the list itself. */
void listEmpty(list *list)
{
    unsigned long len;
    listNode *current, *next;

    current = list->head;
    len = list->len;
    while(len--) {
        next = current->next;
        if (list->free) list->free(current->value);
        zfree(current);
        current = next;
    }
    list->head = list->tail = NULL;
    list->len = 0;
}

新增一個元素到連結串列頭:

/* 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;
    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;
}

刪除一個指定節點:

/* 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->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);
    zfree(node);
    list->len--;
}

這裡對連結串列的增刪改查如果之前學習過資料結構就非常容易理解了

獲取連結串列的迭代器Iterator:

/* Returns a list iterator 'iter'. After the initialization every
 * call to listNext() will return the next element of the list.
 *
 * This function can't fail. */
listIter *listGetIterator(list *list, int direction)
{
    listIter *iter;

    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
    if (direction == AL_START_HEAD)
        iter->next = list->head;
    else
        iter->next = list->tail;
    iter->direction = direction;
    return iter;
}

通過連結串列的迭代器Iterator獲取連結串列的下一個元素:

/* Return the next element of an iterator.
 * It's valid to remove the currently returned element using
 * listDelNode(), but not to remove other elements.
 *
 * The function returns a pointer to the next element of the list,
 * or NULL if there are no more elements, so the classical usage patter
 * is:
 *
 * iter = listGetIterator(list,<direction>);
 * while ((node = listNext(iter)) != NULL) {
 *     doSomethingWith(listNodeValue(node));
 * }
 *
 * */
listNode *listNext(listIter *iter)
{
    listNode *current = iter->next;

    if (current != NULL) {
        if (iter->direction == AL_START_HEAD)
            iter->next = current->next;
        else
            iter->next = current->prev;
    }
    return current;
}

搜尋返回給定值的節點:

/* Search the list for a node matching a given key.
 * The match is performed using the 'match' method
 * set with listSetMatchMethod(). If no 'match' method
 * is set, the 'value' pointer of every node is directly
 * compared with the 'key' pointer.
 *
 * On success the first matching node pointer is returned
 * (search starts from head). If no matching node exists
 * NULL is returned. */
listNode *listSearchKey(list *list, void *key)
{
    listIter iter;
    listNode *node;

    listRewind(list, &iter);
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {
            if (list->match(node->value, key)) {
                return node;
            }
        } else {
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}