redis list
連結串列
連結串列提供了高效的節點重排能力, 以及順序性的節點訪問方式, 並且可以透過增刪節點來靈活地調整連結串列的長度。
作為一種常用資料結構, 連結串列內建在很多高階的程式語言裡面, 因為 Redis 使用的 C 語言並沒有內建這種資料結構, 所以 Redis 構建了自己的連結串列實現。
連結串列在 Redis 中的應用非常廣泛, 比如列表鍵的底層實現之一就是連結串列: 當一個列表鍵包含了數量比較多的元素, 又或者列表中包含的元素都是比較長的字串時, Redis 就會使用連結串列作為列表鍵的底層實現。
舉個例子, 以下展示的 integers 列表鍵包含了從 1 到 1024 共一千零二十四個整數:
redis> LLEN integers (integer) 1024 redis> LRANGE integers 0 10 1) "1"2) "2"3) "3"4) "4"5) "5"6) "6"7) "7"8) "8"9) "9"10) "10"11) "11"
integers
列表鍵的底層實現就是一個連結串列, 連結串列中的每個節點都儲存了一個整數值。
除了連結串列鍵之外, 釋出與訂閱、慢查詢、監視器等功能也用到了連結串列, Redis 伺服器本身還使用連結串列來儲存多個客戶端的狀態資訊, 以及使用連結串列來構建客戶端輸出緩衝區(output buffer), 本書後續的章節將陸續對這些連結串列應用進行介紹。
本章接下來的內容將對 Redis 的連結串列實現進行介紹, 並列出相應的連結串列和連結串列節點 API 。
因為已經有很多優秀的演算法書籍對連結串列的基本定義和相關演算法進行了詳細的講解, 所以本章不會介紹這些內容, 如果不具備關於連結串列的基本知識的話, 可以參考《》一書的 3.3 至 3.5 節, 或者《》一書的 3.2 節, 又或者《》一書的 10.2 節。
連結串列和連結串列節點的實現
每個連結串列節點使用一個 adlist.h/listNode 結構來表示:
typedef struct listNode { // 前置節點 struct listNode *prev; // 後置節點 struct listNode *next; // 節點的值 void *value; } listNode;
多個 listNode 可以透過 prev 和 next 指標組成雙端連結串列, 如圖 3-1 所示。
雖然僅僅使用多個 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 函式則用於對比連結串列節點所儲存的值和另一個輸入值是否相等。
圖 3-2 是由一個 list 結構和三個 listNode 結構組成的連結串列:
Redis 的連結串列實現的特性可以總結如下:
雙端: 連結串列節點帶有 prev 和 next 指標, 獲取某個節點的前置節點和後置節點的複雜度都是 O(1) 。
無環: 表頭節點的 prev 指標和表尾節點的 next 指標都指向 NULL , 對連結串列的訪問以 NULL 為終點。
帶表頭指標和表尾指標: 透過 list 結構的 head 指標和 tail 指標, 程式獲取連結串列的表頭節點和表尾節點的複雜度為 O(1) 。
帶連結串列長度計數器: 程式使用 list 結構的 len 屬性來對 list 持有的連結串列節點進行計數, 程式獲取連結串列中節點數量的複雜度為 O(1) 。
多型: 連結串列節點使用 void* 指標來儲存節點值, 並且可以透過 list 結構的 dup 、 free 、 match 三個屬性為節點值設定型別特定函式, 所以連結串列可以用於儲存各種不同型別的值。
連結串列和連結串列節點的 API
表 3-1 列出了所有用於操作連結串列和連結串列節點的 API 。
表 3-1 連結串列和連結串列節點 API
函式 | 作用 | 時間複雜度 |
---|---|---|
listSetDupMethod | 將給定的函式設定為連結串列的節點值複製函式。 | O(1) 。 |
listGetDupMethod | 返回連結串列當前正在使用的節點值複製函式。 | 複製函式可以透過連結串列的 dup 屬性直接獲得, O(1) |
listSetFreeMethod | 將給定的函式設定為連結串列的節點值釋放函式。 | O(1) 。 |
listGetFree | 返回連結串列當前正在使用的節點值釋放函式。 | 釋放函式可以透過連結串列的 free 屬性直接獲得, O(1) |
listSetMatchMethod | 將給定的函式設定為連結串列的節點值對比函式。 | O(1) |
listGetMatchMethod | 返回連結串列當前正在使用的節點值對比函式。 | 對比函式可以透過連結串列的 match 屬性直接獲得, O(1) |
listLength | 返回連結串列的長度(包含了多少個節點)。 | 連結串列長度可以透過連結串列的 len 屬性直接獲得, O(1) 。 |
listFirst | 返回連結串列的表頭節點。 | 表頭節點可以透過連結串列的 head 屬性直接獲得, O(1) 。 |
listLast | 返回連結串列的表尾節點。 | 表尾節點可以透過連結串列的 tail 屬性直接獲得, O(1) 。 |
listPrevNode | 返回給定節點的前置節點。 | 前置節點可以透過節點的 prev 屬性直接獲得, O(1) 。 |
listNextNode | 返回給定節點的後置節點。 | 後置節點可以透過節點的 next 屬性直接獲得, O(1) 。 |
listNodeValue | 返回給定節點目前正在儲存的值。 | 節點值可以透過節點的 value 屬性直接獲得, O(1) 。 |
listCreate | 建立一個不包含任何節點的新連結串列。 | O(1) |
listAddNodeHead | 將一個包含給定值的新節點新增到給定連結串列的表頭。 | O(1) |
listAddNodeTail | 將一個包含給定值的新節點新增到給定連結串列的表尾。 | O(1) |
listInsertNode | 將一個包含給定值的新節點新增到給定節點的之前或者之後。 | O(1) |
listSearchKey | 查詢並返回連結串列中包含給定值的節點。 | O(N) , N 為連結串列長度。 |
listIndex | 返回連結串列在給定索引上的節點。 | O(N) , N 為連結串列長度。 |
listDelNode | 從連結串列中刪除給定節點。 | O(1) 。 |
listRotate | 將連結串列的表尾節點彈出,然後將被彈出的節點插入到連結串列的表頭, 成為新的表頭節點。 | O(1) |
listDup | 複製一個給定連結串列的副本。 | O(N) , N 為連結串列長度。 |
listRelease | 釋放給定連結串列,以及連結串列中的所有節點。 | O(N) , N 為連結串列長度。 |
重點回顧
連結串列被廣泛用於實現 Redis 的各種功能, 比如列表鍵, 釋出與訂閱, 慢查詢, 監視器, 等等。
每個連結串列節點由一個 listNode 結構來表示, 每個節點都有一個指向前置節點和後置節點的指標, 所以 Redis 的連結串列實現是雙端連結串列。
每個連結串列使用一個 list 結構來表示, 這個結構帶有表頭節點指標、表尾節點指標、以及連結串列長度等資訊。
因為連結串列表頭節點的前置節點和表尾節點的後置節點都指向 NULL , 所以 Redis 的連結串列實現是無環連結串列。
透過為連結串列設定不同的型別特定函式, Redis 的連結串列可以用於儲存各種不同型別的值。
作者:Linkerist
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1806/viewspace-2816314/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- redis命令之-listRedis
- redis list 使用和理解Redis
- redis-8.list型別Redis型別
- Redis的列表型別(List)Redis型別
- Redis之list型別及操作Redis型別
- 「進擊Redis」六、Redis List運用場景、API解析RedisAPI
- 初探Redis-基礎型別ListRedis型別
- (六)Redis 訊息佇列 List、StreamsRedis佇列
- Redis in .NET Core 入門:(4) LIST和SETRedis
- Redis基礎系列-0x005:ListRedis
- redis原始碼分析(四)、redis命令學習總結—連結串列ListRedis原始碼
- Redis 竟然能用 List 實現訊息佇列Redis佇列
- 常用php操作redis命令整理(三)LIST型別PHPRedis型別
- Redis學習手冊(List資料型別)Redis資料型別
- Redis資料結構詳解之List(二)Redis資料結構
- Redis資料結構:List型別全面解析Redis資料結構型別
- [Redis 系列]redis 學習三,redis 資料結構之 string 和 list 基本使用及熟悉Redis資料結構
- 【Redis 系列】redis 學習三,redis 資料結構之 string 和 list 基本使用及熟悉Redis資料結構
- Redis五大資料型別之 List(列表)Redis大資料資料型別
- PHP+Redis實戰教程(3):list列表型別PHPRedis型別
- Redis List 底層三種資料結構原理剖析Redis資料結構
- 《閒扯Redis五》List資料型別底層之quicklistRedis資料型別UI
- redis之列表型別(list)——佇列和棧簡單實現Redis型別佇列
- Redis序列化儲存Java集合List等自定義型別RedisJava型別
- Redis?使用?List?實現訊息佇列的優缺點猜陂Redis佇列
- C# List 用法list<>C#
- 跟著大彬讀原始碼 - Redis 9 - 物件編碼之 三種list原始碼Redis物件
- php基於redis的list型資料結構實現ip限流操作PHPRedis資料結構
- Redis 中使用 list,streams,pub/sub 幾種方式實現訊息佇列Redis佇列
- 別再用 Redis List 實現訊息佇列了,Stream 專為佇列而生Redis佇列
- Redis 使用 List 實現訊息佇列能保證訊息可靠麼?Redis佇列
- debug——python redis的 list ,使用 lrem 刪除不了對應的值PythonRedisREM
- 自定義超實用Redis工具類(滿足物件,list,map等型別)Redis物件型別
- 剖析 Redis List 訊息佇列的三種消費執行緒模型Redis佇列執行緒模型
- Python List 列表list()方法Python
- list
- Hql總結 查詢結果動態組裝成List(map),List(bean),List(list),List(set)等格式(轉)Bean
- 簡單介紹基於Redis的List實現特價商品列表功能Redis