原始碼版本:redis-4.0.1
原始碼位置:
redis中的鍊錶叫adlist
(a generic doubly linked list implementation 乙個通用的雙端鍊錶實現),和普通單鏈表相比,它的方向可以向前
或者向後
,這是由於資料結構中定義了next
和prev
兩個指標決定的,下面看下它的資料結構實現。
typedef
struct listnode listnode;
typedef
struct
list list;
老規矩,我們還是以乙個例子來分析原始碼,這個例子中會設計到adlist的建立、頭插、查詢、反轉輸出、複製、拼接
這些操作,例子的**如下所示:
int keymatch(void *ptr, void *key)
void printlist(list *li)
printf("\n");
}int main(int argc, char **argv)
; listiter iter;
listnode *node;
list *li = listcreate();
for (int i = 0; i < sizeof(b)/sizeof(*b); ++i)
printlist(li);
printf("\nsearch a key :\n");
listsetmatchmethod(li, keymatch);
listnode *ln = listsearchkey(li, "believe");
if (ln != null) else
printf("\nreverse output the list :\n");
printf("li size is %d, elements:", listlength(li));
listrewindtail(li, &iter);
while ((node = listnext(&iter)) != null)
printf("\n");
printf("\nduplicate a new list :\n");
list *lidup = listdup(li);
printlist(lidup);
printf("\nconnect two linked lists :\n");
listjoin(li, lidup);
printlist(li);
listrelease(li);
return0;}
out >
li size is 4, elements:not
or it believe
search a key :
find key is :believe
reverse output the list :
li size is 4, elements:believe it or
not
duplicate a new list :
li size is 4, elements:not
or it believe
connect two linked lists :
li size is 8, elements:not
or it believe not
or it believe
list *li = listcreate();
建立了乙個list,並且返回了指標,**如下所示:
list
*listcreate(void)
listaddnodehead(li, b[i]);
然後將預先定義好的陣列元素依次頭插入了list,與之對應的還有乙個尾插的函式listaddnodetail()
,我們先看下頭插listaddnodehead()
的**:
list
*listaddnodetail(list
*list, void
*value)
else
list
->len++;
return
list;
}
函式首先申請了乙個listnode節點,然後用list->len == 0
判斷了是不是首節點,然後根據不同的策略交換指標,將元素頭插入鍊錶,將長度增加,迴圈插入所有元素之後鍊錶目前情況如下圖所示:
listnode *ln = listsearchkey(li, "believe");
可以查詢第二個引數指定的字串,預設的匹配原則是比較指標是否相等,但是可以自定義match
函式,因為我們的例子中需要比較字串,我自定義了keymatch
函式如下所示:
match函式的宣告是:int (*match)(void *ptr, void *key);
int keymatch(void *ptr, void *key)
listsetmatchmethod(li, keymatch);
可以指定match函式,下面我們看下listsearchkey()
函式的實現:
listnode *listsearchkey(list
*list, void
*key)
} else }}
因為adlist是雙端鍊錶,所以翻轉操作十分簡單,我們直接將迭代器初始化成從鍊錶尾部開始遍歷就完成了翻轉操作。
listrewindtail(li, &iter); //將迭代器從尾部迭代
list *lidup = listdup(li);
會建立一條新的鍊錶返回給使用者,但是需要注意預設的複製策略,如果使用者不自定義dup()
函式,預設返回的複製鍊錶和原始鍊錶共用相同的資料節點,這樣對於乙個節點修改會導致原始鍊錶發生變化。如下所示:
list *lidup = listdup(li); //使用預設的複製操作
strncpy(listindex(lidup, 0)->value, "abc", 3); //修改複製返回的鍊錶的值
printlist(lidup);
printlist(li);
out >
li size is 4, elements:abc or it believe
li size is 4, elements:abc or it believe //可以看到原始鍊錶也受了影響,not 修改為了 abc
但是如果自定義dup函式,不再使得複製之後的鍊錶和原始鍊錶公用節點就可以避免這個問題:
void *strdup(void *ptr)
listsetdupmethod(li, strdup); //設定自定義的dup函式
list *lidup = listdup(li);
strncpy(listindex(lidup, 0)->value, "abc", 3);
printlist
(lidup);
printlist
(li);
out >
lisize
is 4, elements:abc
orit
believe
lisize
is 4, elements:not
orit
believe //還是原始的值沒有變化
listjoin(li, lidup);
可以將兩個鍊錶做連線操作:
void listjoin(list
*l, list
*o)
listrelease(li);
函式負責釋放鍊錶,首先會呼叫listempty()
函式釋放掉所有listnode,最後再釋放掉list本身的空間。
adlist的實現相對來說較簡單,我們上面分析了它的建立、插入、查詢、反轉等操作,基本上熟悉了api和底層資料結構的原理,但是由於鍊錶新增節點時候(無論頭插尾插)每次都是申請新的空間,所以比較容易造成記憶體碎片。這方面想想有無辦法優化。
[完]
Redis原始碼分析 intset h c
intset.h c 是redis 的整數set實現,intset的結構體如下 基本結構 typedef struct intset intset intset的第乙個成員encoding,表明contents中的儲存資料的資料長度,可以是16bits,32bits,64bits。第二個成員leng...
Redis原始碼分析系列
redis目前熱門nosql記憶體資料庫,量不是很大,本系列是本人閱讀redis原始碼時記錄的筆記,由於時間倉促和水平有限,文中難免會有錯誤之處,歡迎讀者指出,共同學習進步,本文使用的redis版本是2.8.19。redis之hash資料結構 redis之intset資料結構 redis之skipl...
redis原始碼分析 adlist
typedef struct listnode listnode 首先定義了乙個節點,包含前驅和後繼以及對應的value typedef struct listiter listiter list的迭代器,next指標和迭代方向 typedef struct list list 鍊錶內容 head和...