redis中的adlist實質上是一個雙向連結清單。裡面實作了自己的iterator(包含正負方向)。作者對于連結清單寫的真的是流暢,看得賞心悅目。沒有一句多餘的,很值得學習。
資料結構
總體來說分為三個資料類型。
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;
listNode結構體。每個節點的資料結構
typedef struct listNode {
struct listNode *prev;//前指正
struct listNode *next;//後指針
void *value;//存儲的資料
} listNode;
iterator結構體
typedef struct listIter {
listNode *next;//下一個節點
int direction;//方向 利用宏定義來判斷
} listIter;
/* Directions for iterators */
#define AL_START_HEAD 0 //從前往後即為0
#define AL_START_TAIL 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;
}
listEmpty清空連結清單元素,但是沒有釋放連結清單
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);//若有free函數則使用free清理value
zfree(current);//删除節點
current = next;//循環
}
list->head = list->tail = NULL;//連結清單初始化
list->len = 0;
}
listRelease清空連結清單元素及自身,調用listEmpty
/* Free the whole list.
*
* This function can't fail. */
void listRelease(list *list)
{
listEmpty(list);
zfree(list);
}
listAddNodeHead頭插法
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;
}
listAddNodeTail尾插法 與頭插法基本一緻
list *listAddNodeTail(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 = list->tail;
node->next = NULL;
list->tail->next = node;
list->tail = node;
}
list->len++;
return 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) {//若after為真則表示新節點插在old_node後否則插在其前面
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) {//若不是頭節點,要調整前面節點的next指針
node->prev->next = node;
}
if (node->next != NULL) {//若不是尾節點,要調整後面節點的pre指針
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);//若定義了free 釋放value空間
zfree(node);//釋放節點
list->len--;//長度減1
}
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;
}
listNext 疊代器傳回目前值,并且向後移
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;
}
listDup 複制一個相同的連結清單,傳回為新的連結清單 (dup dup2為linux中 檔案描述符的複制的函數名)
list *listDup(list *orig)
{
list *copy;
listIter iter;
listNode *node;
if ((copy = listCreate()) == NULL) //
return NULL;
copy->dup = orig->dup; //拷貝相應的api函數
copy->free = orig->free;
copy->match = orig->match;
listRewind(orig, &iter);//擷取一個從後往前的疊代器。這裡疊代器不是指針,省去了釋放空間的操作。申請的空間在棧中。(這個小細節真是贊)
while((node = listNext(&iter)) != NULL) {
void *value;
if (copy->dup) {//如存在複制函數則使用
value = copy->dup(node->value);
if (value == NULL) {
listRelease(copy);
return NULL;
}
} else
value = node->value;//否則直接指派
if (listAddNodeTail(copy, value) == NULL) {//尾查法插入元素
listRelease(copy);//失敗則調用釋放記憶體的函數釋放,并傳回NULL
return NULL;
}
}
return copy;
}
listSearchKey 根據可以key值找節點
listNode *listSearchKey(list *list, void *key)
{
listIter iter; //同樣使用的不是指針,省去釋放煩惱。
listNode *node;
listRewind(list, &iter);//取得反向疊代器 與c++類似
while((node = listNext(&iter)) != NULL) {
if (list->match) {//若有math函數則調用
if (list->match(node->value, key)) {
return node;
}
} else {
if (key == node->value) {//否則直接判等
return node;
}
}
}
return NULL;//若找不到則傳回空
}
對于這一資料結構的學習,複習了對連結清單的基本操作。