redis源码学习之adlist
参考《Redis 设计与实现》 (基于redis3.0.0) 作者:黄健宏
学习redis3.2.13
介绍
链表简单了解:
- 链表是一种线性结构,由很多节点串联在一起,每个节点间由指针连接。
- 每个节点含存储数据部分与指向下一个节点的部分。
- 链表的节点可动态增减,增减头尾节点时间复杂度为O(1)。非常适合数据量预先难以知晓或变化频繁的情况。
注:增减头尾节点时间复杂度为O(1)的前提是,拥有头结点的同时拥有尾节点指针
C语言没有链表这种数据结构,redis造了属于它自己的链表
链表结构
节点定义
typedef struct listNode {
//指向前一个节点
struct listNode *prev;
//指向后一个节点
struct listNode *next;
//指向存储的值
void *value;
} listNode;
根据节点的结构可知redis中的链表为双向链表,取书中的例子如图:
管理结构定义
typedef struct list {
//头节点指针
listNode *head;
//尾节点指针
listNode *tail;
//自定义dup函数指针,用于节点存储值的深拷贝
void *(*dup)(void *ptr);
//自定义free函数指针,用于节点存储值的释放
void (*free)(void *ptr);
//自定义match函数指针,用于检查节点存储值是否与key匹配
int (*match)(void *ptr, void *key);
//节点长度
unsigned long len;
} list;
还是取书中的已有例子,redis链表结构直观描述如下:
节点中的数据域是void *类型,配合管理结构中的函数指针,这使得其支持任意数据的存储
迭代器
redis的list实现了自己的迭代器,list的遍历由迭代器的来提供,使遍历操作得以简化
迭代器结构定义如下
typedef struct listIter {
//指向的节点
listNode *next;
//迭代方向
int direction;
} listIter;
//迭代方向定义如下
#define AL_START_HEAD 0
#define AL_START_TAIL 1
迭代器的获取
//获取链表指定迭代方向的迭代器
listIter *listGetIterator(list *list, int direction)
{
listIter *iter;
if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;
if (direction == AL_START_HEAD)
iter->next = list->head;
else
iter->next = list->tail;
iter->direction = direction;
return iter;
}
迭代器的游走方式
//获取迭代器所指对象并游走迭代器
listNode *listNext(listIter *iter)
{
listNode *current = iter->next;
if (current != NULL) {
if (iter->direction == AL_START_HEAD)
iter->next = current->next;
else
iter->next = current->prev;
}
return current;
}
使用迭代器遍历
...
#define listNodeValue(n) ((n)->value)
...
listIter *iter = listGetIterator(list,<direction>);
while ((node = listNext(iter)) != NULL) {
// doSomethingWith(listNodeValue(node));
}
后记
- 通过void *类型存储数据,函数指针存储处理数据的对应方法,以回调的方式执行,可增加容器的灵活性
- 使用迭代器可以简化遍历操作
原创不易,转载请注明出处,谢谢