Redis定義了雙向鏈表,用來存儲列表鍵的值,還有其餘的我不知的。
雙向鏈表的節點定義爲:node
typedef struct listNode { // 前置節點 struct listNode *prev; // 後置節點 struct listNode *next; // 節點的值 void *value; } listNode;
這裏是把節點的值做爲了一個成員變量,Linux內核裏面節點的定義是這樣的git
struct list_head{ struct list_head *next; struct list_head *prev; }
這樣能夠沒有不用處理節點所帶數據的類型,可是要經過offsetof/container_of這類來處理。不過這是題外話了。
除此以外,Redis還定義了鏈表結構,以下:github
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;
這樣定義的好處有:redis
快速獲取鏈表的頭尾算法
快速查詢鏈表的節點數,像動態字符串sdshr裏的len那樣,不用遍歷所有字符就能夠獲取字符串的長度,這裏也相似。函數
它裏面還定義了鏈表的迭代器,設計
typedef struct listIter { // 當前迭代到的節點 listNode *next; // 迭代的方向 int direction; } listIter;
裏面迭代的方向爲指針
// 從表頭向表尾進行迭代 #define AL_START_HEAD 0 // 從表尾到表頭進行迭代 #define AL_START_TAIL 1
迭代器能夠經過如下幾個接口來使用(不徹底列出來)code
listIter *listGetIterator(list *list, int direction) //根據迭代方向給鏈表建立迭代器 listNode *listNext(listIter *iter) //返回迭代器當前指向的節點,而且把迭代器根據方向向前移動一下
假如我要遍歷整個鏈表,我能夠這樣作接口
iter = listGetIterator(list, AL_START_HEAD); while ((node = listNext(iter)) != NULL) { doSomethingWith(node); }
寫起來是比較簡單易懂的。
裏面還有個反轉節點的函數
void listRotate(list *list) { listNode *tail = list->tail; if (listLength(list) <= 1) return; /* Detach current tail */ // 取出表尾節點 list->tail = tail->prev; list->tail->next = NULL; /* Move it as head */ // 插入到表頭 list->head->prev = tail; tail->prev = NULL; tail->next = list->head; list->head = tail; }
這個實際上是翻轉節點,不是倒序。是否是跟之前作過的算法題很像。有沒有想起手搖算法。
最後貼上《Redis設計與實現》裏的總結
鏈表被普遍用於實現 Redis 的各類功能, 好比列表鍵, 發佈與訂閱, 慢查詢, 監視器, 等等。
每一個鏈表節點由一個 listNode 結構來表示, 每一個節點都有一個指向前置節點和後置節點的指針, 因此 Redis 的鏈表實現是雙端鏈表。
每一個鏈表使用一個 list 結構來表示, 這個結構帶有表頭節點指針、表尾節點指針、以及鏈表長度等信息。
由於鏈表表頭節點的前置節點和表尾節點的後置節點都指向 NULL , 因此 Redis 的鏈表實現是無環鏈表。
經過爲鏈表設置不一樣的類型特定函數, Redis 的鏈表能夠用於保存各類不一樣類型的值。
參考:
1.《Redis設計與實現》
2.https://github.com/huangz1990...