链表作为一种常用的数据结构,提供了高效的节点重排能力,以及顺序性节点访问方式。并且可以通过增删来灵活的调整链表的长度。在许多高级语言中都有链表的实现。然而,Redis使用了C语言并没有实现这种数据结构。因此Redis自己实现了链表。
链表和链表节点的实现
每一个链表使用一个adlist.h/listNode结构来实现
typedef struct listNode{
struct listNode *prev; //前置节点
struct listNode *next; //前置节点
void *value; //节点的值
}listNode;
多个listNode可以通过由prev和next指针组成的双向链表如下图:
由prev和next指针组成的双向链表虽然使用listNode结构可以组成双向链表,但是为了操作方便可以使用adlist.h/list来持有链表。定义如下:
typedef struct list{
listNode * head; //头结点
listNode * tail; //尾节点
unsigned long len; //链表所包含的节点数量
void *(*dup) (void *ptr); //节点值复制函数
void (*free) (void *ptr); //节点值释放函数
void (*match)(void *ptr,void *key); //节点值对比函数
}list;
list中为链表提供了表头指针head和表尾指针tail以及计算链表持有节点数据量的len,而dup函数用于复制链表节点所保存的值,free函数用于释放链表节点所保存的值,match函数用于对比链表节点保存的的值和;另一个输入的值是否相等。
由list和listNode组成的链表Redis链表的特征
- 双向:链表节点带有prev和next指针,获取某一个节点的前置节点和后置节点的复杂度都是O(1)。
- 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL结束。
- 带表头和表尾指针:通过list的head和tail指针,程序获得链表的表头和表尾节点的复杂度为O(1)。
- 带链表长度计数器:通过list的len,程序获取链表长度的复杂度为O(1)。
- 多态:链表节点使用void*指针来保存节点值,可以通过函数dup,free,match对节点的值进行操作,所以链表可以保存不同的类型的值。
总结
- 链表被广泛用于实现Redis的各种功能。如:链表键,发布与订阅,慢查询,监视器等。
链表 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 为链表长度 |
网友评论