Redis的列表对象底层所使用的数据结构其中之一就是list。

list

Redis的list是一个双端链表,其由3部分构成:链表节点、链表迭代器、链表。这一设计思想和STL的list是一样的,STL的list也是由这三部分组成。需要特别说明的是Redis用C语言实现了list的迭代器,比较巧妙,下面就来分析list源码。

list节点

节点的值为void*类型,从而可以保存不同类型的值,甚至是另一种类型的对象。

// 双端链表的节点
typedef struct listNode {
struct listNode *prev; // 指向上一个节点
struct listNode *next; // 指向下一个节点
void *value; // 指向节点的值, void*类型,使得节点可以保存不同类型的值
} listNode;

list迭代器

c语言实现c++中的迭代器;双端链表的迭代器,方便了遍历链表的操作;根据direction,可设置为前向/反向迭代器

typedef struct listIter {
listNode *next; // 指向迭代器方向上下一个链表结点
int direction; // AL_START_HEAD=0:从头部往尾部方向移动;AL_START_TAIL=1:往尾部往头部方向移动
} listIter;

其中direction的取值有:

/* Directions for iterators */
// 迭代器方向的宏定义
#define AL_START_HEAD 0
#define AL_START_TAIL 1

list

与一般设计类似,list中有指向头尾节点的指针,以及链表节点数量的计数。不同的是,由于链表节点为void*类型,被设计为可以存储不同类型的数据,甚至是另一种类型的对象,所以添加了与节点相关的3个函数,作用分别是复制、释放、比较节点的值。

// 双端链表
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;

list的操作函数

Redis用宏定义实现了一些复杂度为O(1)的链表操作,以提高list操作的效率。

/* Functions implemented as macros */
// 通过宏来实现一些O(1)时间复杂度的函数
#define listLength(l) ((l)->len)
#define listFirst(l) ((l)->head)
#define listLast(l) ((l)->tail)
#define listPrevNode(n) ((n)->prev)
#define listNextNode(n) ((n)->next)
#define listNodeValue(n) ((n)->value) #define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m)) #define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)

list的源码比较好理解,本人对其已经做了详细的注释,就不仔细介绍了,下面附上源码及注释。list相关的文件有两个:adlist.h, adlist.c

adlist.h

#ifndef __ADLIST_H__
#define __ADLIST_H__ /* Node, List, and Iterator are the only data structures used currently. */ // redis的链表为双端链表
// 节点的值为void*类型,从而可以保存不同类型的值
// 结合dup,free,match函数实现链表的多态 // 双端链表的节点
typedef struct listNode {
struct listNode *prev; // 指向上一个节点
struct listNode *next; // 指向下一个节点
void *value; // 指向节点的值, void*类型,使得节点可以保存不同类型的值
} listNode; // c语言实现c++中的迭代器!!!
// 双端链表的迭代器,方便了遍历链表的操作
// 根据direction,可设置为前向/反向迭代器
typedef struct listIter {
listNode *next; // 指向迭代器方向上下一个链表结点
int direction; // AL_START_HEAD=0:从头部往尾部方向移动;AL_START_TAIL=1:往尾部往头部方向移动
} 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; /* Functions implemented as macros */
// 通过宏来实现一些O(1)时间复杂度的函数
#define listLength(l) ((l)->len)
#define listFirst(l) ((l)->head)
#define listLast(l) ((l)->tail)
#define listPrevNode(n) ((n)->prev)
#define listNextNode(n) ((n)->next)
#define listNodeValue(n) ((n)->value) #define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m)) #define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match) /* Prototypes */
// list数据结构相关的函数
// 具体含义见adlist.c
list *listCreate(void);
void listRelease(list *list);
list *listAddNodeHead(list *list, void *value);
list *listAddNodeTail(list *list, void *value);
list *listInsertNode(list *list, listNode *old_node, void *value, int after);
void listDelNode(list *list, listNode *node);
listIter *listGetIterator(list *list, int direction);
listNode *listNext(listIter *iter);
void listReleaseIterator(listIter *iter);
list *listDup(list *orig);
listNode *listSearchKey(list *list, void *key);
listNode *listIndex(list *list, long index);
void listRewind(list *list, listIter *li);
void listRewindTail(list *list, listIter *li);
void listRotate(list *list); /* Directions for iterators */
// 迭代器方向的宏定义
#define AL_START_HEAD 0
#define AL_START_TAIL 1 #endif /* __ADLIST_H__ */

adlist.c

/* adlist.c - A generic doubly linked list implementation
*/ #include <stdlib.h>
#include "adlist.h"
#include "zmalloc.h" /* Create a new list. The created list can be freed with
* AlFreeList(), but private value of every node need to be freed
* by the user before to call AlFreeList().
*
* On error, NULL is returned. Otherwise the pointer to the new list. */ // 创建一个链表
// 返回值:list/NULL
list *listCreate(void)
{
struct list *list; if ((list = zmalloc(sizeof(*list))) == NULL) // 为链表分配内存
return NULL;
// 初始化链表结构体的成员
list->head = list->tail = NULL;
list->len = ;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
return list; // 返回为新链表分配的内存的起始地址
} /* Free the whole list.
*
* This function can't fail. */ // 释放链表及链表节点
void listRelease(list *list)
{
unsigned long len;
listNode *current, *next; current = list->head;
len = list->len;
while(len--) {
next = current->next;
if (list->free) list->free(current->value); // 释放链表节点的值
zfree(current); // 释放链表节点
current = next;
}
zfree(list); // 释放链表
} /* Add a new node to the list, to head, containing the specified 'value'
* pointer as value.
*
* On error, NULL is returned and no operation is performed (i.e. the
* list remains unaltered).
* On success the 'list' pointer you pass to the function is returned. */ // 从双端链表的头部插入新节点
// 返回值:list/NULL
list *listAddNodeHead(list *list, void *value)
{
listNode *node; if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == ) { // 原链表为一空链表
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
// 插入到双端链表的头结点之前
node->prev = NULL;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
list->len++;
return list;
} /* Add a new node to the list, to tail, containing the specified 'value'
* pointer as value.
*
* On error, NULL is returned and no operation is performed (i.e. the
* list remains unaltered).
* On success the 'list' pointer you pass to the function is returned. */ // 从双端链表的尾部插入新节点
// 返回值:list/NULL
list *listAddNodeTail(list *list, void *value)
{
listNode *node; if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (list->len == ) {
list->head = list->tail = node;
node->prev = node->next = NULL;
} else {
node->prev = list->tail;
node->next = NULL;
list->tail->next = node;
list->tail = node;
}
list->len++;
return list;
} // 在链表list的节点old_node的前或后插入新节点
// after为0,则在old_node之前插入;否则,在old_node之后插入
// 返回值:list/NULL
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
listNode *node; if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;
if (after) { // old_node之后插入
node->prev = old_node;
node->next = old_node->next;
if (list->tail == old_node) {
list->tail = node;
}
} else { // old_node之前插入
node->next = old_node;
node->prev = old_node->prev;
if (list->head == old_node) {
list->head = node;
}
}
if (node->prev != NULL) {
node->prev->next = node;
}
if (node->next != NULL) {
node->next->prev = node;
}
list->len++;
return list;
} /* Remove the specified node from the specified list.
* It's up to the caller to free the private value of the node.
*
* This function can't fail. */ // 删除链表list中节点node
void listDelNode(list *list, listNode *node)
{
if (node->prev)
node->prev->next = node->next;
else
list->head = node->next;
if (node->next)
node->next->prev = node->prev;
else
list->tail = node->prev;
if (list->free) list->free(node->value);
zfree(node);
list->len--;
} /* Returns a list iterator 'iter'. After the initialization every
* call to listNext() will return the next element of the list.
*
* This function can't fail. */ // 返回链表的迭代器
// 返回值:list/NULL
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;
} /* Release the iterator memory */ // 释放迭代器的内存
void listReleaseIterator(listIter *iter) {
zfree(iter);
} /* Create an iterator in the list private iterator structure */ // 回绕迭代器到链表头部
void listRewind(list *list, listIter *li) {
li->next = list->head;
li->direction = AL_START_HEAD;
} // 回绕迭代器到链表尾部
void listRewindTail(list *list, listIter *li) {
li->next = list->tail;
li->direction = AL_START_TAIL;
} /* Return the next element of an iterator.
* It's valid to remove the currently returned element using
* listDelNode(), but not to remove other elements.
*
* The function returns a pointer to the next element of the list,
* or NULL if there are no more elements, so the classical usage patter
* is:
*
* iter = listGetIterator(list,<direction>);
* while ((node = listNext(iter)) != NULL) {
* doSomethingWith(listNodeValue(node));
* }
*
* */ // 返回迭代器所指向的元素,并将迭代器往其方向上移动一步
// 返回值:指向当前节点的指针/NULL
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;
} /* Duplicate the whole list. On out of memory NULL is returned.
* On success a copy of the original list is returned.
*
* The 'Dup' method set with listSetDupMethod() function is used
* to copy the node value. Otherwise the same pointer value of
* the original node is used as value of the copied node.
*
* The original list both on success or error is never modified. */ // 复制输入链表
// list*/NULL
list *listDup(list *orig)
{
list *copy;
listIter iter;
listNode *node; if ((copy = listCreate()) == NULL) // 创建新链表
return NULL;
copy->dup = orig->dup;
copy->free = orig->free;
copy->match = orig->match;
listRewind(orig, &iter); // 回绕迭代器到链表头部
while((node = listNext(&iter)) != NULL) { // 遍历原链表,顺序取出节点
void *value; if (copy->dup) {
value = copy->dup(node->value); // 通过list.dup函数复制节点值
if (value == NULL) {
listRelease(copy); // 出错释放链表
return NULL;
}
} else
value = node->value;
if (listAddNodeTail(copy, value) == NULL) { // 从新链表尾部插入值
listRelease(copy); // 出错释放链表
return NULL;
}
}
return copy;
} /* Search the list for a node matching a given key.
* The match is performed using the 'match' method
* set with listSetMatchMethod(). If no 'match' method
* is set, the 'value' pointer of every node is directly
* compared with the 'key' pointer.
*
* On success the first matching node pointer is returned
* (search starts from head). If no matching node exists
* NULL is returned. */ // 返回链表中节点值与key相匹配的节点
// listNode*/NULL
listNode *listSearchKey(list *list, void *key)
{
listIter iter;
listNode *node; listRewind(list, &iter);
while((node = listNext(&iter)) != NULL) {
if (list->match) {
if (list->match(node->value, key)) { // 调用list.match函数对节点值进行比较
return node;
}
} else {
if (key == node->value) {
return node;
}
}
}
return NULL;
} /* Return the element at the specified zero-based index
* where 0 is the head, 1 is the element next to head
* and so on. Negative integers are used in order to count
* from the tail, -1 is the last element, -2 the penultimate
* and so on. If the index is out of range NULL is returned. */ // 返回给定索引位置的节点
// index=0,返回头结点
// index < 0,则从尾部开始返回,index = -1,返回尾部节点
listNode *listIndex(list *list, long index) {
listNode *n; if (index < ) {
index = (-index)-;
n = list->tail;
while(index-- && n) n = n->prev;
} else {
n = list->head;
while(index-- && n) n = n->next;
}
return n;
} /* Rotate the list removing the tail node and inserting it to the head. */ // 将尾部节点弹出,插入到链表头节点之前,成为新的表头节点
void listRotate(list *list) {
listNode *tail = list->tail; if (listLength(list) <= ) return; /* Detach current tail */
list->tail = tail->prev;
list->tail->next = NULL;
/* Move it as head */
list->head->prev = tail;
tail->prev = NULL;
tail->next = list->head;
list->head = tail;
}

(全文完)

附:Redis系列:http://www.cnblogs.com/zxiner/p/7197415.html

Redis—数据结构之list的更多相关文章

  1. Redis 数据结构使用场景

    转自http://get.ftqq.com/523.get 一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的 ...

  2. Redis数据结构

    Redis数据结构 Redis数据结构详解(一)   前言 Redis和Memcached最大的区别,Redis 除啦支持数据持久化之外,还支持更多的数据类型而不仅仅是简单key-value结构的数据 ...

  3. Redis数据结构底层知识总结

    Redis数据结构底层总结 本篇文章是基于作者黄建宏写的书Redis设计与实现而做的笔记 数据结构与对象 Redis中数据结构的底层实现包括以下对象: 对象 解释 简单动态字符串 字符串的底层实现 链 ...

  4. Redis 数据结构与内存管理策略(上)

    Redis 数据结构与内存管理策略(上) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  5. Redis 数据结构与内存管理策略(下)

    Redis 数据结构与内存管理策略(下) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  6. Redis数据结构之intset

    本文及后续文章,Redis版本均是v3.2.8 上篇文章<Redis数据结构之robj>,我们说到redis object数据结构,其有5中数据类型:OBJ_STRING,OBJ_LIST ...

  7. Redis数据结构之robj

    本文及后续文章,Redis版本均是v3.2.8 我们知道一个database内的这个映射关系是用一个dict来维护的.dict的key固定用一种数据结构来表达,这这数据结构就是动态字符串sds.而va ...

  8. Redis 数据结构之dict(2)

    本文及后续文章,Redis版本均是v3.2.8 上篇文章<Redis 数据结构之dict>,我们对dict的结构有了大致的印象.此篇文章对dict是如何维护数据结构的做个详细的理解. 老规 ...

  9. Redis 数据结构之dict

    上篇文章<Redis数据结构概述>中,了解了常用数据结构.我们知道Redis以高效的方式实现了多种数据结构,因此把Redis看做为数据结构服务器也未尝不可.研究Redis的数据结构和正确. ...

  10. Redis数据结构以及应用场景

    1. Redis数据结构以及应用场景 1.1. Memcache VS Redis 1.1.1. 选Memcache理由 系统业务以KV的缓存为主,数据量.并发业务量大,memcache较为合适 me ...

随机推荐

  1. Python 字节码是什么

    了解 Python 字节码是什么,Python 如何使用它来执行你的代码,以及知道它是如何帮到你的. 如果你曾经编写过 Python,或者只是使用过 Python,你或许经常会看到 Python 源代 ...

  2. Python 变量 (上)

    Python通过变量引用内存中的值,变量的值占用多少空间是由变量的类型决定的.声明变量不需要指定变量的类型,解释器会自动根据值来判断.变量名称必须符合标识符的定义 标识符 标识符是由字母,数字和下划线 ...

  3. BZOJ3246 IOI2013Dreaming

    如果将森林里每棵树都各自看做一个点,那么最后所连成的树应该是一颗菊花,否则将叶子节点父亲改为根不会更劣. 对于每个点所代表的树,其和根节点相连的点应该是到其他点距离最大值最小的点.这个点显然是直径的中 ...

  4. Day22-Django之Form组件验证

    1. Django里面的Form专门用来做验证. 用Form创建一个类,把用户发来的数据放到request.POST里面发给这个类,这个类会帮忙做验证. 返回3个结果:是否验证成功了,所有的正确信息, ...

  5. 【APIO 2018】铁人两项(圆方树)

    题目链接 题意大概是,求有多少三元组$(s,c,f)(s \neq c, c \neq f, s \neq f)$,满足从$s$到$f$有一条简单路径经过$c$. 得到结论: 点双中任意互不相同的三个 ...

  6. linux内核设计与实现一书阅读整理 之第十八章

    CHAPTER 18 调试 18.1 准备开始 需要的是准备是: - 一个bug - 一个藏匿bug的内核版本 - 相关内核代码的知识和运气 重点: 想要成功的进行调试,就取决于是否能让这些错误重现. ...

  7. Python 爬虫入门(二)—— IP代理使用

    上一节,大概讲述了Python 爬虫的编写流程, 从这节开始主要解决如何突破在爬取的过程中限制.比如,IP.JS.验证码等.这节主要讲利用IP代理突破. 1.关于代理 简单的说,代理就是换个身份.网络 ...

  8. P1392 取数

    P1392 取数 题目描述 在一个n行m列的数阵中,你须在每一行取一个数(共n个数),并将它们相加得到一个和.对于给定的数阵,请你输出和前k小的取数方法. 说明 对于20%的数据,n≤8 对于100% ...

  9. java.lang.AutoCloseable

    java.lang.AutoCloseable和java.io.Closeable public interface AutoCloseable { void close() throws Excep ...

  10. Spring整合Quartz定时任务 在集群、分布式系统中的应用

    概述 虽然单个Quartz实例能给予你很好的Job调度能力,但它不能满足典型的企业需求,如可伸缩性.高可靠性满足.假如你需要故障转移的能力并能运行日益增多的 Job,Quartz集群势必成为你应用的一 ...