Redis原始碼剖析——adlist的實現
阿新 • • 發佈:2019-02-05
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)得到長度