链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度。
由于C语言并没有内置这种数据结构,所以Redis构建了自己的链表实现。
环境:当一个列表键包含了数量较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现。
应用:列表键的底层实现之一就是链表(有前提条件的)、发布与订阅、慢查询、监视器等功能也用到了链表、Redis服务器本身还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区(outer buffer)。
链表与链表节点的实现:
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成员则是用于实现多态链表所需的类型特定函数。
Redis链表实现的特性:
双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1);
无环:表头结点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL结束;
带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获得链表的表头结点和表尾节点的复杂度都是O(1);
带链表长度计数器:list结构的len属性。
多态:链表节点使用的 void* 指针来保存节点值,通过list结构的dup函数、free函数、match三个属性为节点设置的类型特定函数。所以链表可以用来保存不同类型的值。