本文所引用的源码全部来自Redis2.8.2版本。
Redis中linkedlist数据结构与API相关文件是adlist.c, adlist.h。
本文主要讲解Redis中对双向链表的详细实现,以及相关API的分析,对于双向链表本身可以从任意一本数据结构书本中得到详解。
一、双向链表简介双向链表作为一种常见的数据结构,在严蔚敏数据结构书里有详细的讲解,双向链表的每个数据节点都有两个指针,分别指向后继与前驱节点,因此从双向链表中的任意一个节点开始都可以很方便地访问其前驱与后继节点。
二、Redis中双向链表数据结构以及相关宏定义typedef struct listNode {
struct listNode *prev;//前驱指针
struct listNode *next;//后继指针
void *value; //节点的值
} listNode;
typedef struct listIter {//链表迭代器
listNode *next;
int direction;//遍历方向
} listIter;
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;
宏名称
| 作用
| listLength
| 获取链表的长度值
| listFirst
| 获取链表的首指针
| listLast
| 获取链表的尾指针
| listPrevNode
| 获取当前节点的前驱节点指针
| listNextNode
| 获取当前节点的后继节点指针
| listNodeValue
| 获取当前节点所存储的值
| listSetDupMethod
| 设置链表节点value的复制函数
| listSetFreeMethod
| 设置链表节点value的释放内存函数
| listSetMatchMethod
| 设置链表节点value的比较函数
| listGetDupMethod
| 获取链表节点value的复制函数
| listGetFree
| 获取链表节点value的释放内存函数
| listGetMatchMethod
| 获取链表节点value的比较函数
| 三、Redis中双向链表API介绍名称
| 作用
| 复杂度
| listCreate
| 创建一个新双向链表
| O(1)
| listRelease
| 释放一个双向链表以及包含的节点内存
| O(N)
| listAddNodeHead
| 将一个节点添加到链表的表头
| O(1)
| listAddNodeTail
| 将一个节点添加到链表的表尾
| O(1)
| listInsertNode
| 将一个节点添加到给定节点的之后或之前
| O(1)
| listDelNode
| 删除给定的节点
| O(1)
| listGetIterator
| 生成双向链表的迭代器
| O(1)
| listReleaseIterator
| 释放双向链表的迭代器
| O(1)
| listNext
| 通过迭代器获取下一个节点
| O(1)
| listDup
| 创建给定链表的副本
| O(N)
| listSearchKey
| 查找与给定key相同值的节点
| O(N)
| listIndex
| 根据给定的索引值,返回相应的节点
| O(N)
| listRewind
| 重新初始化迭代器,迭代方向从头至尾
| O(1)
| listRewindTail
| 重新初始化迭代器,迭代方向从尾至头
| O(1)
| listRotate
| 取出链表尾节点并插入到头部
| O(1)
|
四、Redis双向链表性能分析
Redis中的双向链表也许是Redis中最简单最容易实现的数据结构,对于API就不多说了,都很简单,也没啥可以说的,下面简单分析一下双向链表的性能。
listNode拥有prev前驱指针和next后继指针,因此通过迭代器可以很方便的对链表从从头至尾或从尾至头遍历;
list拥有header头指针和tail为指针,对于在链表的头部或尾部进行插入节点的时间复杂度全部为O(1),高效地实现了Redis中一些指令的操作;
list自带保存链表长度的字段len,使得计算链表长度的时间复杂度为O(1)。
五、小结
双向链表主要有两个作用:作为Redis列表数据类型的底层实现方法之一;作为通用数据结构可以被其他功能模块使用。
双向链表实现简单,Redis对双向链表加以改造,添加保存节点长度的字段,以及实现自己的迭代指针,使得一些数据操作变得简单。
最后感谢黄健宏(huangz1990)的Redis设计与实现及其他对Redis2.6源码的相关注释对我在研究Redis2.8源码方面的帮助。
|