日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis源码剖析(八)链表

發布時間:2024/4/19 数据库 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis源码剖析(八)链表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在之前對Redis的介紹中,可以看到鏈表的使用頻率非常高。

鏈表可以作為單獨的存儲結構,比如客戶端的監視鏈表記錄該客戶端監視的所有鍵,服務器的模式訂閱鏈表記錄所有客戶端和它的模式訂閱。

鏈表也可以內嵌到字典中作為字典的值類型,比如數據庫的監視字典使用鏈表存儲監視某個鍵的所有客戶端,服務器的訂閱字典使用鏈表存儲訂閱某個頻道的所有客戶端。

鏈表結構

節點

Redis中的鏈表是雙向鏈表,即每一個節點都保存了它的前驅節點和后繼節點,用于提高操作效率。節點定義如下

//adlist.h /* 鏈表節點 */ typedef struct listNode {struct listNode *prev; /* 前驅節點 */struct listNode *next; /* 后繼節點 */void *value; /* 值 */ } listNode;

鏈表

鏈表結構主要記錄了表頭節點和表尾節點,節點個數以及一些函數指針,定義如下

//adlist.h /* 鏈表 */ 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還為鏈表提供迭代器的功能,主要是對鏈表節點的封裝,另外通過鏈表節點的前驅節點和后繼節點,可以輕松的完成向前移動和向后移動

//adlist.h /* 迭代器 */ typedef struct listIter {/* 指向實際的節點 */listNode *next;/* 迭代器方向,向前還是向后 */int direction; } listIter;

direction的值有兩個,向前和向后,由宏定義指出

//adlist.h #define AL_START_HEAD 0 /* 從頭到尾(向后) */ #define AL_START_TAIL 1 /* 從尾到頭(向前) */

鏈表操作

創建鏈表

鏈表的創建工作由listCreate函數完成,實際上就是申請鏈表內存然后初始化成員變量

//adlist.c /* 創建一個空鏈表 */ 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; }

刪除鏈表

刪除一個鏈表比創建稍微麻煩一點,因為需要釋放每個節點中保存的值,沒錯,它正是調用free函數完成的

//adlist.c /* 釋放鏈表的內存空間 */ void listRelease(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);/* 釋放節點內存 */zfree(current);current = next;}/* 因為list* 也是動態申請的,所以也需要釋放 */zfree(list); }

在末尾插入節點

在其他模塊的實現上,經常會看到向鏈表尾部添加節點的操作,它的實現由listAddNodeTail完成。函數首先為新節點申請內存,然后將節點添加到鏈表中,這里需要根據鏈表之前是否為空執行不同操作

  • 鏈表為空,新節點將作為鏈表的頭節點和尾節點,新節點的前驅和后繼指針都為空
  • 鏈表非空,新節點將作為鏈表的尾節點,之前的尾節點的后繼指針指向新節點,新節點的前驅指針指向之前的尾節點
//adlist.c /* 在鏈表尾部添加節點 */ 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; }

迭代器移動

迭代器主要用于遍歷鏈表,而迭代器的重點在移動上,通過direction變量,可以得知迭代器移動的方向,又通過鏈表節點的前驅后繼節點,可以輕松實現移動操作

//adlist.c /* 移動迭代器,同時返回下一個節點 */ listNode *listNext(listIter *iter) {/* next指針是當前迭代器指向的節點指針 */listNode *current = iter->next;if (current != NULL) {/* 根據方向為next賦值 */if (iter->direction == AL_START_HEAD)iter->next = current->next;elseiter->next = current->prev;}/* 返回之前迭代器指向的節點 */return current; }

重置迭代器

此外,Redis提供了重置迭代器的操作,分別由listRewind和listRewindTail函數完成

/* 重置迭代器方向為從頭到尾,使迭代器指向頭節點 */ void listRewind(list *list, listIter *li) {li->next = list->head;li->direction = AL_START_HEAD; }/* 重置迭代器方向為從尾到頭,使迭代器指向尾節點 */ void listRewindTail(list *list, listIter *li) {li->next = list->tail;li->direction = AL_START_TAIL; }

鏈表搜索

有了迭代器的基礎,就可以實現鏈表搜索功能,即在鏈表中查找與某個值匹配的節點,需要利用迭代器遍歷鏈表

//adlist.c /* 查找值key,返回鏈表節點 */ listNode *listSearchKey(list *list, void *key) {listIter iter;listNode *node;/* 設置迭代器方向為從頭到尾,使其指向鏈表頭節點 */listRewind(list, &iter);/* 遍歷鏈表 */while((node = listNext(&iter)) != NULL) {/* 如果提供值匹配函數,則調用,否則使用==比較 */if (list->match) {if (list->match(node->value, key)) {return node;}} else {if (key == node->value) {return node;}}}return NULL; }

宏定義函數

除了上面提到的函數外,Redis還提供了一些宏定義函數,比如返回節點值,返回節點的前驅后繼節點等

//adlist.h /* 返回鏈表節點個數 */ #define listLength(l) ((l)->len) /* 返回頭節點 */ #define listFirst(l) ((l)->head) /* 返回尾節點 */ #define listLast(l) ((l)->tail) /* 返回前驅節點 */ #define listPrevNode(n) ((n)->prev) /* 返回后繼節點 */ #define listNextNode(n) ((n)->next) /* 返回節點值 */ #define listNodeValue(n) ((n)->value)/* 設置鏈表的值復制,值析構,值匹配函數 */ #define listSetDupMethod(l,m) ((l)->dup = (m)) #define listSetFreeMethod(l,m) ((l)->free = (m)) #define listSetMatchMethod(l,m) ((l)->match = (m))/* 獲取鏈表的值賦值,值析構,值匹配函數 */ #define listGetDupMethod(l) ((l)->dup) #define listGetFree(l) ((l)->free) #define listGetMatchMethod(l) ((l)->match)

小結

由于鏈表結構簡單,所以在實現上還是非常容易理解的。當然Redis中與鏈表有關的函數還有很多很多,這里僅僅介紹了一些常用操作,有興趣可以深入源碼查看

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的Redis源码剖析(八)链表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。