Redis底层数据结构之链表
来源:互联网 发布:电子书什么软件好 编辑:程序博客网 时间:2024/06/07 11:41
Redis底层数据结构之链表
一、Redis中链表的实现
我们都知道在列表的插入与删除的操作,如果数组的中间插入一个元素,那么这个元素后的所有元素的内存地址都要往后移动。删除的话同理,只有对数据的最后一个元素进行插入删除操作时,才比较快。链表并不需要更改节点的内存地址,链表的优势在于增和删,查找时间复杂度为O(n),链表的扩展性比数组好。链表作为一种重要的数据结构广泛用于实现redis的各种功能,由于在数据结构/算法中很多时候都学过链表,这里就不啰嗦了,直接上代码;
Redis中对链表数据结构的定义在Adlist.h文件中listNode结构体,具体的结果如下:
/* Node, List, and Iterator are the only data structures used currently. */ /* listNode结点 */ typedef struct listNode { //结点的前一结点 struct listNode *prev; //结点的下一结点 struct listNode *next; //节点的值 void *value; } listNode;
这是链表节点的基本定义,但是为了实现链表的各项操作,方便用户调用,Redis又做了一层封装,使用list来持有链表,具体的结构定义如下:
/* listNode 列表 */ typedef struct list { //链表头结点 listNode *head; //链表尾结点 listNode *tail; /* 下面3个指针函数为所有结点公用的方法,相当于我们在面向对象编程中类的方法*/ // 复制链表节点所保存的值 void *(*dup)(void *ptr); // 释放链表节点所保存的值 void (*free)(void *ptr); // 匹配两个节点的值是否相等 int (*match)(void *ptr, void *key); // 链表长度 unsigned long len; } list;
还有一个数据结构是迭代器,熟悉C++ STL或者Java集合的,应该再熟悉不过了,迭代器就要是封装集合(这里是链表)的遍历规则,让用户不必拘泥于遍历细节,下面是Redis中链表的结构定义
/* list迭代器,只能为单向 */ typedef struct listIter { //当前迭代位置的下一结点 listNode *next; //迭代器的方向 int direction; } listIter;
列举完链表的数据结构完之后,下面我们重点挑几个函数配合代码讲解链表的具体实现过程,首先看查找函数listSearchKey,
/* 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的节点,存在则返回改节点,否则返回NULL */ listNode *listSearchKey(list *list, void *key) { // 这里用到了迭代器,接下来我们可以看到时如何迭代遍历的 listIter *iter; listNode *node; //获取迭代器,遍历方向是从head往后 iter = listGetIterator(list, AL_START_HEAD); //遍历循环 while((node = listNext(iter)) != NULL) { //如果list定义了match方法,则调用match方法比较节点的值 if (list->match) { if (list->match(node->value, key)) { //如果函数返回true,则代表找到结点,释放迭代器空间,返回找到的节点 listReleaseIterator(iter); return node; } } else { //如果没有定义list 的match方法,则直接比较函数指针 if (key == node->value) { //如果相等,则代表找到结点,释放迭代器 listReleaseIterator(iter); return node; } } } listReleaseIterator(iter); return NULL; }
接下我们再看一个函数的实现,头插法加入节点listAddNodeHead
/* Add a new node to the list, to head, contaning 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 *listAddNodeHead(list *list, void *value) { listNode *node; //定义新的listNode,并赋值函数指针 if ((node = zmalloc(sizeof(*node))) == NULL) return NULL; node->value = value; if (list->len == 0) { //当此时没有任何结点时,头尾结点是同一个结点,前后指针为NULL list->head = list->tail = node; node->prev = node->next = NULL; } else { //设置此结点next与前头结点的位置关系 node->prev = NULL; node->next = list->head; list->head->prev = node; list->head = node; } //结点计数递增并返回 list->len++; return list; }
链表其他的函数,这里就不一一分析了,有兴趣的朋友,把Redis源码,下载下来,一看便知。
下面来总结下Redis采用双端链表实现的好处有哪些:
- 双端链表可实现在O(1)的实践复杂度访问前后节点
- 无环,表头的前驱节点和表尾的后继节点指向NULL
- 带表头和表尾节点,程序获取表头和表尾的时间复杂度为O(1)
- 使用链表长度计数器,可在O(1)的时间复杂度内获取链表长度
- 链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。
0 0
- Redis底层数据结构之链表
- Redis底层数据结构之跳跃表
- Redis底层数据结构之字典
- REDIS系列之底层数据结构
- Redis底层数据结构之简单动态字符串
- Redis底层数据结构总结
- Redis数据结构之链表
- Redis源码分析(2)-底层数据结构之字典
- redis列表键的底层实现之链表
- 深入理解Redis:底层数据结构
- Redis数据结构底层知识总结
- 深入理解Redis:底层数据结构
- Redis学习总结_1_底层数据结构
- redis数据结构底层(个人记忆使用)
- redis 源代码之数据结构(1)--链表的实现
- redis 源代码之数据结构(sds,链表的实现)
- Redis源码学习3-基本数据结构之双向链表
- Redis内部数据结构详解之压缩链表(ziplist)
- VERcode
- linux系统日志syslog 简介
- 用迅雷下载Genymotion的virtual device的办法
- 遗传算法
- m估计及其推到公式
- Redis底层数据结构之链表
- 最新iOS应用Beta测试分发app分发平台源码 Android应用内测分发平台源码免费仿蒲公英平台
- redis 集群配置实战
- linux/debian安装wps以及缺失字体,亲测可用
- DataTable相关资料
- Leetcode141——Linked List Cycle
- 浅谈Java的Comparator接口实现自定义排序Arrays.sort()
- linux中的粘滞位
- 剑指Offer 19 : 二叉树镜像