1 前言
在上一篇文章中,概要介绍了Redis的第一种数据结构SDS(Simple Dynamic String)之后,本文将进一步介绍Redis中的另一种数据结构——链表。
2 什么是链表
链表,作为数据结构中的基础结构,理解起来非常简单。以下是摘自百度百科中,对链表的定义。
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
3 Redis链表的定义及实现
在C语言中,没有内置链表数据结构,所以,Redis构建了自己的链表实现。参考Redis源码可知,链表的数据结构定义分成两部分。
第一部分:ListNode
从上图可知,Redis中的链表节点,采用双端链表节点的设计方式,即每个节点都有指向自己前置节点和后置节点的指针域,同时包含节点本身的值域。
而采用双端链表,在之后实现对链表的读写上,能够极大降低时间复杂度。
第二部分:List
从上图可知,双端链表的数据结构中,包括以下内容
1 *head :指向链表头结点的指针
2 *tail: 指向链表表尾节点的指针
3 *(*dup)(void *ptr):节点值复制函数
4 (*free)(void *ptr):节点值释放函数
5 *match)(void *ptr, void *key):节点值对比函数
6 len :链表包含的节点数量
此外,双端链表的数据结构定义中,还有一种辅助迭代器数据结构,用于迭代链表,即——双端链表迭代器
这里说明一下迭代方向 direction值:adlist.h的头文件中,预定义了两个int类型变量,用于指示链表的迭代方向。
4 Redis中使用链表的优势
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活调整链表的长度。
而Redis将链表数据结构定义成双端链表,将具有以下性能优势:
1 双端:每个链表节点都带有prev和next指针,所以在获取某个节点的前置节点和后置节点的复杂度都是O(1)
2 带表头和表尾指针:通过list结构的head指针和tail指针,程序获取链表表头和表尾节点的时间复杂度为O(1)
3 自带长度计数器:链表数据结构中的len属性,用来对list持有的节点数进行统计,能够在时间复杂度为O(1)的前提下获取链表中的节点数量。
4 多态:链表的数据结构中,内置了三个函数(dup、free、match),同事链表节点使用void*指针来保存节点值,并且可以通过这三个函数设置特定的数据类型,所以,链表可以用来保存各种不同类型的值。