当前位置: 首页 > news >正文

redis源码学习之数据结构---双向链表

基本数据结构在adlist.h中定义:

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

listNode是链表的节点结构,有向前(prev)和向后 (next)两个指针,双向链表,保存的值是void*类型。

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结构标识一个链表,提供头(head)、尾(tail)两个指针,分别指向头部的结点和尾部的结点;提供三个函数指针,供用户传入自定义函数,用于复制(dup)、释放(free)和 匹配(match)链表中的结点的值(value);通过无符号整数len,标示链表的长度。由此可见,获取链表长度的时间复杂度为O(1)

typedef struct listIter {
 listNode *next;
 int direction;
} listIter;

listIter是访问链表的迭代器,指针(next)指向链表的某个结点,direction标示迭代访问的方向(宏AL_START_HEAD表示向前,AL_START_TAIL表示向后),宏在adlist.h中定义。

#define AL_START_HEAD 0
#define AL_START_TAIL 1

接下来看一下redis提供的操作链表的方法:

/* 使用宏函数实现的方法 */
//获取长度
#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)
//获取结点的值 是一个void类型指针
#define listNodeValue(n) ((n)->value)

/* 下面3个函数主要用来设置list结构中3个函数指针,参数m为method的意思 */
#define listSetDupMethod(l,m) ((l)->dup = (m))
#define listSetFreeMethod(l,m) ((l)->free = (m))
#define listSetMatchMethod(l,m) ((l)->match = (m))

/* 下面3个函数主要用来获取list结构的单个函数指针 */
#define listGetDupMethod(l) ((l)->dup)
#define listGetFree(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)

list *listCreate(void),申请一个链表节点,初始化一个空链表

list *listCreate(void)
{
    struct list *list;
	//使用redis自己实现的zmalloc()分配空间,申请一个list节点
    if ((list = zmalloc(sizeof(*list))) == NULL)
        return NULL;
    //赋初值,初始化为空链表
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;
    return list;
}

void listEmpty(list *list),删除链表中的所有节点,但不会删除链表本身
void listRelease(list *list),删除整个链表,包括链表本身的节点

/* Remove all the elements from the list without destroying the list itself. */
void listEmpty(list *list)
{
    unsigned long len;
    listNode *current, *next;

    current = list->head;
    len = list->len;
    while(len--) {
        next = current->next;
        //这里如果有自定义的释放函数,调用自定义的,否则调用redis自己实现的释放函数
        if (list->free) list->free(current->value);
        zfree(current);
        current = next;
    }
    list->head = list->tail = NULL;
    list->len = 0;
}

/* Free the whole list.
 *
 * This function can't fail. */
void listRelease(list *list)
{
    listEmpty(list);
    zfree(list);
}

list *listAddNodeHead(list *list, void *value) 头插法

list *listAddNodeHead(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {
        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;
}

list *listAddNodeTail(list *list, void *value) 尾插法

list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    if (list->len == 0) {
        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 *listInsertNode(list *list, listNode *old_node, void *value, int after) 给定值,给定节点,插入给定节点的之前或之后,由参数after决定之前或之后

/*
如果 after 为 0 ,将新节点插入到 old_node 之前
如果 after 不为 0 ,将新节点插入到 old_node 之后
*/
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) {
        node->prev = old_node;
        node->next = old_node->next;
        if (list->tail == old_node) {
            list->tail = node;
        }
    } else {
        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;
}

void listDelNode(list *list, listNode *node) 删除一个节点

/*对节点私有值(private value of the node)的释放工作由调用者进行。该函数一定会成功.
  T = O(1)
*/
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--;
}

相关文章:

  • redis源码分析--事件驱动模型
  • ubuntu下zmq编译安装及请求-应答模式测试
  • c++输出:怎么解决数字过大时默认使用科学计数法输出的问题?
  • c++11实现一个自动注册的工厂模式
  • zmq发布-订阅模式c++实现
  • linux报错:bash: syntax error near unexpected token `(‘ --路径中有括号怎么处理?
  • golang学习总结--函数
  • golang学习总结--结构体、接口
  • 解决运行时报错:error while loading shared libraries xxx.so,cannot open shared object file
  • 超实用:linux shell光标移动常用快捷键
  • git commit之后如何撤销
  • golang学习总结--协程、channel
  • 跟我一起写dockerfile
  • dockerfile中多个FROM指令的意义(multistage)
  • dockerfile实战:使用dockerfile制作c/c++程序docker镜像
  • JavaScript 如何正确处理 Unicode 编码问题!
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 08.Android之View事件问题
  • Consul Config 使用Git做版本控制的实现
  • CSS 提示工具(Tooltip)
  • Meteor的表单提交:Form
  • rc-form之最单纯情况
  • 从setTimeout-setInterval看JS线程
  • 前端性能优化--懒加载和预加载
  • 小李飞刀:SQL题目刷起来!
  • 以太坊客户端Geth命令参数详解
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • ​Kaggle X光肺炎检测比赛第二名方案解析 | CVPR 2020 Workshop
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • ###C语言程序设计-----C语言学习(6)#
  • #Linux(Source Insight安装及工程建立)
  • #Linux(帮助手册)
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (function(){})()的分步解析
  • (二)Eureka服务搭建,服务注册,服务发现
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • .apk文件,IIS不支持下载解决
  • .NET Core跨平台微服务学习资源
  • .NET MVC第五章、模型绑定获取表单数据
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .Net面试题4
  • /*在DataTable中更新、删除数据*/
  • ??myeclipse+tomcat
  • [2010-8-30]
  • [BZOJ] 2044: 三维导弹拦截
  • [c]统计数字
  • [dfs] 图案计数
  • [Hadoop in China 2011] 蒋建平:探秘基于Hadoop的华为共有云
  • [Hive] 常见函数