redis 系列4 資料結構之連結串列
一. 概述
連結串列提供了高效的節點重排能力,以及順序性的節點訪問方式,並且可能通過增刪節點來靈活地調整連結串列的長度。作為一種資料結構,在C語言中並沒有內建的這種資料結構。所以Redis構建了自己的連結串列實現。連結串列在Redis中應用非常多,比如列表鍵的底層實現之一就是連結串列,當一個列表鍵包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字串時,Redis就會使用連結串列作為列表鍵的底層實現。
-- 例1:使用integers 列表鍵包含了從1到10,共有10個整數 127.0.0.1:6379> rpush integers "1" "2" "3" "4" "5" "6""7" "8" "9" "10" (integer) 10 127.0.0.1:6379> llen integers (integer) 10 127.0.0.1:6379> lrange integers 0 5 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 6) "6"
integers列表鍵的底層實現就是一個連結串列。連結串列中的每個節點都儲存了一個整數值,除了連結串列鍵之外,釋出與訂閱,慢查詢,監視器等功能也用到了連結串列。Redis伺服器本身還使用連結串列來儲存多個客戶端的狀態資訊,以及使用連結串列來構建客戶端輸出緩衝區(output buffer)。
二 連結串列和連結串列節點定義
// 每個連結串列節點使用一個adlist.h/listNode結構來表示: typedef struct listNode { //前置節點 struct listNode *prev; //後置節點 struct listNode *next; //節點的值 void *value; }listNode; //別名
多個listNode可能通過prev和next指標組成雙端連結串列。雖然使用多個listNode結構就可以組成連結串列,但使用adlist.h/list 來持有連結串列的話,操作起來會更方便。
typedef struct list { //表頭節點 listNode *head; //表尾節點 listNode *tail; //連結串列所包含的節點數量 unsigned long len; //節點值複製函式 void *(*dup) (void *ptr); //節點值釋放函式 void (*free)(void *ptr) //節點值對比函式 int (*match) (void *ptr,void *key) }list; //別名
在list結構中連結串列提供了表頭指標head, 表尾指標tail,以及連結串列長度計數器len, 而dup, free和match成員則是用於實現多型連結串列所需的型別特定函式:dup 函式用於複製連結串列節點所儲存的值; free 函式用於釋放連結串列節點所儲存的值; match 函式則用於對比連結串列節點所儲存的值和另一個輸入值是否相等。
三.Redis的連結串列實現的特性總結
(1)雙端: 連結串列節點帶有prev 和next 指標,獲取某個節點的前置節點和後置節點的複雜度都是0(1) 。
(2)無環: 表頭節點的prev指標和表尾節點的next 指標都指向null ,對連結串列的訪問以null 為終點 。
(3)帶表頭指標和表尾指標:通過list結構的head指標和tail 指標,程式獲取連結的表頭節點和表尾節點的複雜度為0(1)
(4)帶連結串列長度計數器:程式使用list結構的len屬性來對list持有的連結串列節點進行計數,程式獲取連結串列中節點數量的複雜度為0(1)
(5)多型:連結串列節點使用void* 指標來儲存節點值,並且可能通過list結構的dup,free,match 三個屬性為節點值設定型別特定函式,所以連結串列可以用於儲存各種不同型別的值。
四. 連結串列和連結串列節點的API
函式 |
作用 |
listSetDupMethod |
將給定的函式設定為連結串列的節點值複製函式 |
listGetDupMethod |
返回連結串列當前正在使用的節點值複製函式 |
listSetFreeMethod |
將給定的函式設定為連結串列的節點值釋放函式 |
listGetFree |
返回連結串列當前正在使用的節點值釋放函式 |
listSetMatchMethod |
將給定的函式設定為連結串列的節點值對比函式 |
listGetMatchMethod |
返回連結串列當前正在使用的節點值對比函式 |
listLenth |
返回連結串列的長度(包含多少節點) |
listFirst |
返回連結串列的表頭節點 |
listLast |
返回連結串列的表尾節點 |
listPrevNode |
返回給定節點的前置節點 |
listNextNode |
返回給定節點的後置節點 |
listNodeValue |
返回給定節點目前正在儲存的值 |
listCreate |
建立一個不包含任何節點的新連結串列 |
listAddNodeHead |
將一個包含給定值的新節點新增到給定連結串列的表頭 |
listAddNodeTail |
將一個包含給定值的新節點新增到給定連結串列的表尾 |
listInsertNode |
將一個包含給定值的新節點新增到給定節點的之前或者之後 |
listSearchKey |
查詢並返回連結串列中包含給定值的節點 |
listIndex |
返回連結串列在給定索引上的節點 |
listDelNode |
從連結串列中刪除給定節點 |
listRotate |
將連結串列的表尾節點彈出,然後將被彈出的節點插入到連結串列的表頭,成為新的表頭節點 |
listDup |
複製一個給定連結串列的副本 |
listRelease |
釋放給定連結串列,以及連結串列中的所有節點 |
總結:
(1) 連結串列被廣泛用於實現Redis的各種功能,比如列表鍵,釋出與訂閱,慢查詢,監視器等。
(2) 每個連結串列節點由一個listNode結構來表示,每個節點都有一個指定前置節點和後置節點的指標,所以Redis的連結串列實現是雙端連結串列。
(3) 每個連結串列使用一個list結構來表示,這個結構帶有表頭節點指標 ,表尾節點指標,以及連結串列長度等資訊。
(4) 因為連結串列表頭節點的前置節點和表尾節點的後置節點都指向null, 所以Redis的連結串列實現是無環連結串列。
(5) 通過為連結串列設定不同的型別特定函式,Redis的連結串列可以用於儲存各種不同型別的值。