没错,本篇我们要说一说链表在Redis中的应用。本打算写Redis中的列表,但想来想去觉得列表仅仅是Redis链表的一部分,并不能完全说明链表这一数据结构在Redis中的应用。那就说说数据结构链表吧。小伙伴们,开始啦!
注:链表作为列表的底层实现是有条件的。当列表元素数量较多,或者是元素都是特别长的字符串时,才会使用链表作为列表的底层实现;当元素数量特别少,并且元素是特别短的字符串或者是特别小的整型数字时,将使用压缩列表来作为列表的底层实现。如图所示:
2.1 数据结构:链表
C语言中没有内置这种数据结构(例如Java实现了链表结构 LinkedList),所以Redis需要自己实现链表结构。链表是一种基本的数据结构,就不再详细的说明了。那对于Redis的链表是怎么实现的呢?
2.1.1 链表节点
Redis中的链表节点是怎么实现的呢?每一个链表节点都是使用 ListNode 结构体实现的,ListNode 结构体定义在adlist.h 头文件中。定义如下:
typedef struct ListNode{
// 前(后)置节点
struct ListNode *prev;
struct ListNode *next;
// 值
void *value;
}ListNode;
由上述定义可知:
- Redis中链表是一个双向链,每一个节点都指向前后节点。
- 每个节点值的类型都是void,可知节点可以保存任意类型的值。也就是说,一个链表中可以保存不同类型的数据。
由多个ListNode组成的链表结构,如下图所示:
站在链表结构上来讲,多个节点已经可以组成一个基本的链表了,但对于操作来说,还是需要一些其他信息来管理这个链表。这些信息包括链表长度、头(尾)节点、判断两个节点是否相等、释放节点空间、复制节点,上图仅仅由ListNode 组成的链表无法方便的完成。(注意:“无法方便的完成” 是指可以通过复杂的操作完成,就是复杂、不方便)
2.1.2 list结构体
由于仅仅由ListNode组成的链表不易管理,使用list来持有链表,那么,操作起来就easy多了。list结构体定义在adlist.h中,定义如下:
typedef struct list{
// 节点头、尾、链表长度
ListNode *head;
ListNode *tail;
unsigned int len;
// 复制节点、释放节点、对比节点
void *(*dup)(void *ptr);
void *(*free)(void *prt);
int *(*match)(void *ptr,void *key);
}
由上述定义可知:
- 获取链表长度的复杂度O(1),无需遍历整个链表
- 获取头尾节点的复杂度O(1)
还有一条就是链表是无环的