redis中的 adlist链表实现

news2024/9/28 23:25:31

adlist源码解读(基于redis 6.2.7)

1丶打开源码 adlist.h

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;  连表的长度,O(1)
} list;

​ 可见,list的结构还是比较简单的.listNode,代表每个list的结点, list整体包含了头结点,尾部结点. 而 listIter 则是在迭代操作中使用,下面具体分析.

2丶创建

list *listCreate(void)
{
    struct list *list;
	//分配空间,分配失败则返回null
    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;
}

创建比较简单,即分配内存空间. 在我的电脑64位, sizeof(list) = 48; 5个指针一个long类型 = 6 * 8=48

3丶增加节点

3.1头插法

/* Add a new node to the list, to head, containing the specified 'value'
 * pointer as value.
 * 在list中添加一个新的node,放在头部.包含一个特殊的 "value", value的指针.
 * On error, NULL is returned and no operation is performed (i.e. the
 * list remains unaltered). 
   若失败则返回NULL, 没有操作执行
 * On success the 'list' pointer you pass to the function is returned. 
 	若成功,则返回入参 传递过来的list指针
 */
list *listAddNodeHead(list *list, void *value)
{
    //新增的节点
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    //新增加点赋值
    node->value = value;
    //list长度为0,属于刚创建的状态
    if (list->len == 0) {
        //首位节点都是这个新增的节点
        list->head = list->tail = node;
        //前后节点都是NULL;
        node->prev = node->next = NULL;
    } else {
        //新增节点的前节点置位NULL,因为是头插法,这个节点作为头结点,肯定没有前置节点.
        node->prev = NULL;
        //新增节点 后置节点 指向原来的head节点
        node->next = list->head;
        //原来的head节点,  前置节点指向 这个新增节点.
        list->head->prev = node;
        //list的头结点设置为新增加点
        list->head = node;
    }
    list->len++;
    return list;
}

3.2 尾插法

基本与头插法相似

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;
}

3.3 指定节点前后插入节点

//after为ture则向后面插入
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;
}

4丶删除节点

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--;
}

5丶查询节点

listNode *listSearchKey(list *list, void *key)
{
    listIter iter;
    listNode *node;
	//将 iter 的下一个节点设置为头结点
    listRewind(list, &iter);
    //便利
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {
            if (list->match(node->value, key)) {
                return node;
            }
        } else {
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/636962.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Submit的使用,程序中调用其他报表

Submit的使用 项目需求:编写一个程序能够将实时的物料库存数据通过发送邮件的形式发送到对应的邮件。现有标准的事务码MB5B来查看对应的库存数据。可以在程序中使用submit将参数传递到MB5B中,然后将获取的数据返回到程序中,然后在执行发送邮件…

【Git通关之旅】从山脚到山顶(傲视群雄版)

Git分布式版本控制工具 版本控制器的方式 集中式版本控制工具 集中式版本控制工具,版本库是集中存放在中央服务器的,team里每个人work时从中央服务器下载代 码,是必须联网才能工作,局域网或互联网。个人修改后然后提交到中央版本库…

【蓝桥杯】求既约分数—>(全解)最大公约数与最小公倍数

前言: 通过对【蓝桥杯】2020初赛的一道求既约分数的题目的讲解,引出关于求“最大公约数与最小公倍数”的方法汇总。 对于“最大公约数与最小公倍数”来说,求解都有一些固定的方法,而这些方法一般都是固定的,只需要掌握…

亚马逊、Allegro卖家建立属于自己的测评系统,实现批量优质账号养成

卖家搭建一套完整的测评系统,卖家自己能够养出批量优质账号,并完全掌控真实买家的浏览、加购、下单和评价等风控数据规律。我们的系统能够自主加速推广,防御反击,同时节省运营成本,实现高效的测评运营。 我们的系统支…

传统印刷企业需要部署数字工厂管理系统吗

随着数字化技术的快速发展,传统印刷企业面临着巨大的市场竞争压力。为了提高生产效率,降低生产成本,越来越多的企业开始考虑部署印刷数字工厂管理系统。但是,对于许多企业来说,是否部署印刷数字工厂仍然是一个需要权衡…

【国产复旦微FMQL45教程】-小试牛刀之LED

本教程采用 FMQL7045 FPGA开发板来完成整个试验,板卡照片如下: 具有丰富的接口资源,系统框图如下: 本教程用于完成基于Vivado的FMQL45的LED实验,目标是能够将这款开发板PL端先跑起来。 对于纯 PL 设计,我们…

002、体系结构之TiDB Server

TiDB Server 1、TiDB总览1.1、TiDB Server架构1.2、TiDB Server 主要功能: 2、SQL语句处理语句的解析和编译SQL层协议层上下文解析层逻辑优化器物理优化器本地执行器分布式执行器 3、如何将表的数据转成kv形式4、在线DDL相关模块5、GC机制与相关模块6、TiDB Server …

pyecharts案例三——河南省疫情地图绘制

代码实现 代码的业务流程基本和全国疫情地图相同,注意城市名结尾必须有“市”才能识别 import json from pyecharts.charts import Map from pyecharts.options import *f open("./疫情.txt", "r", encoding"UTF-8") data f.read…

暴力破解测试-业务安全测试实操(1)

业务安全测试实践模版理论指导_luozhonghua2000的博客-CSDN博客 测试原理和方法 暴力破解测试是指针对应用系统用户登录账号与密码进行的穷举测试,针对账号或密码进行逐一比较,直到找出正确的账号与密码。 般分为以下三种情况: 在已知账号的情况下,加载密码字典针对密码进行…

从美颜算法到AI美颜SDK:美丽的背后隐藏着什么?

在年轻人的生活中,通过美颜SDK类型的美颜工具进行拍摄已经成为了一种全新的文化现象。时下,AI美颜、美颜SDK讨论热点极高,那么大家知道美颜算法和AI美颜到底有什么不同吗?它们背后隐藏着什么样的技术和思想? 一、美颜算…

java的StringBuilder、Stringjoiner

一、StringBuilder StringBuilder可以看成是一个容器,创建之后里面的内容是可变的。作用是提高字符串的操作效率。 注意:使用String创建字符串对象时,是不能改变字符串的内容的,例如: String s1 "aaa"; St…

【解决MySQL-jdbc连接问题】com.mysql.jdbc.Driver was not found, trying direct instantiat

启动服务时出现报错 com.zaxxer.hikari.util.DriverDataSource : Registered driver with driverClassNamecom.mysql.jdbc.Driver was not found, trying direct instantiation.而且接口有时候能访问成功,有时候的超时连接,异常的慢 经查询&#xff0c…

BUUCTF Alice与Bob 1

题目描述: 密码学历史中,有两位知名的杰出人物,Alice和Bob。他们的爱情经过置换和轮加密也难以混淆,即使是没有身份认证也可以知根知底。就像在数学王国中的素数一样,孤傲又热情。下面是一个大整数:98554799767,请分解…

HTMLCSS Day03 CSS字体及文本样式

文章目录 1.文本属性- 颜色属性在CSS中如何通过color属性来修改文字颜色英文单词rgbrgba十六进制 - font-style 用于打开和关闭斜体文本- font-weight 为字体设置粗细程度- font-size 为文字指定大小- font-family 为文字指定特殊的字体,浏览器只会使用浏览器可以访…

动态规划dp —— 23.等差数列划分

1.状态表示 是什么?dp表中里的值所表示的含义就是状态表示 dp[i]表示:以i位置为结尾的所有子数组中有多少个等差数列 2.状态转移方程 dp[i] 等于什么 以i位置为结尾的等差数列,也就是说i位置必须和i-1,i-2位置构成等差数列&am…

98.实战网页构建定价部分-第一节

通过之前的文章我们实现如下的页面&#xff1a; ● 这节课我们来完成价目的部分 <section class"section-pricing"><div class"container"><span class"subheading">定价</span><h2 class"heading-secondary…

目前音质好的几款音频功放芯片

“音频功率放大器”简称音频功放&#xff0c;是扩声系统不可缺少的音响设备&#xff1b;是指把来自音源或前级放大器输出的弱信号放大并推动一定功率的音箱发出声音的集成电路。 音频功放可分为模拟功放和数字功放&#xff0c;传统模拟功放主要有A、AB、B、G类等&#xff0c;区…

vscode 实时同步代码到远程服务器

&#xff08;1&#xff09;在本地新建一个工程文件夹quant&#xff0c;将要同步的文件或者代码或文件夹放到quant里面&#xff1a; &#xff08;2&#xff09;创建sftp配置 使用 ctrlshiftp 快捷键调出输入框&#xff0c;选择 SFTP:Config 回车 会在 .vscode 目录下创建一个 s…

实验篇(7.2) 11. 创建点对点安全隧道(FortiGate-IPsec) ❀ 远程访问

【简介】前面我们实验的是FortiClient客户端与防火墙进行VPN连接&#xff0c;现在我们要做的实验是防火墙与防火墙之间进行VPN连接。现在我们来看看两台防火墙之间要怎样创建VPN连接。 实验要求与环境 OldMei集团深圳总部部署了域服务器和ERP服务器&#xff0c;用来对集团总部进…

基于Java+Spring+vue+element实现唯美鲜花商城购物系统

基于JavaSpringvueelement实现唯美鲜花商城购物系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文…