1. 程式人生 > >Redis原始碼剖析——adlist的實現

Redis原始碼剖析——adlist的實現

adlist

adlist為Redis基本資料結構之一,為雙向連結串列,記錄了連結串列長度,adlist的迭代器記錄了迭代節點和方向,個人覺得實現優於STL的list

幾個重要結構

adlist實現比較精簡,基本上寫過連結串列相關的程式碼就能很快寫出所有實現函式

/*
 * 雙端連結串列節點
 */
typedef struct listNode {
    // 前置節點
    struct listNode *prev;
    // 後置節點
    struct listNode *next;
    // 節點的值
    void *value;
} listNode;

/*
 * 雙端連結串列迭代器
 */
typedef struct listIter { // 當前迭代到的節點 listNode *next; // 迭代的方向 int direction; } listIter; // 從表頭向表尾進行迭代 #define AL_START_HEAD 0 // 從表尾到表頭進行迭代 #define AL_START_TAIL 1 /* * 雙端連結串列結構 */ 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;

基本函式總覽

函式 功能 複雜度
listCreate 建立連結串列 O(1)
listRelease 釋放,回收空間 O(N)
listInsertNode 插入節點 O(1)
listDelNode 刪除節點 O(1)
listSearchKey 查詢節點 O(N)
listGetIterator 獲取迭代器 O(1)

其它的巨集/函式實現都很簡單。不做介紹了

listCreate

// 建立一個無節點的空連結串列
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;
}

listRelease

// 釋放所有節點再釋放List
void listRelease(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;
    }

    // 釋放連結串列結構
    zfree(list);
}

listInsertNode

// 在所給節點的前/後插入新節點
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
    listNode *node;

    // 建立新節點
    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;

    // 儲存值
    node->value = value;

    // 將新節點新增到給定節點之後
    if (after) {
        node->prev = old_node;
        node->next = old_node->next;
        // 給定節點是原表尾節點
        if (list->tail == old_node) {
            list->tail = node;
        }
    // 將新節點新增到給定節點之前
    } else {
        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;
}

listDelNode

// 刪除節點
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--;
}

listSearchKey

// 根據給定Key值查詢節點,找到則返回第一個
listNode *listSearchKey(list *list, void *key) {
    listIter *iter;
    listNode *node;

    // 迭代整個連結串列
    iter = listGetIterator(list, AL_START_HEAD);
    while((node = listNext(iter)) != NULL) {

        // 對比
        if (list->match) {
            if (list->match(node->value, key)) {
                listReleaseIterator(iter);
                // 找到
                return node;
            }
        } else {
            if (key == node->value) {
                listReleaseIterator(iter);
                // 找到
                return node;
            }
        }
    }

    listReleaseIterator(iter);

    // 未找到
    return NULL;
}

listGetIterator

// 根據方向獲取迭代器
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;
}

小結

adlist實現比較簡單,與STL的list相比複雜性小了很多,但基本的功能都有。STL提供了合併排序等功能,adlist記錄了連結串列長度,能O(1)得到長度