从[redis:LinkedList]中学习链表
文章目录
- adlist
- listNode
- list
- macros[宏定义]
- listCreate
- listInitNode
- listEmpty
- listRelease
- listAddNodeHead
- listLinkNodeHead
- listAddNodeTail
- listLinkNodeTail
- listInsertNode
- listDelNode
- listUlinkNode
- listIndex
- redis3.2.100quicklist
- redis7.2.2quicklist
redis的基本数据类型之一"list"的底层编码从"ziplist"和"LinkedList"[3.2版本之前]升级到"由ziplist组成的LinkedList"[3.2版本及以后]再升级到"由listpack组成的LinkedList"[7.2.2版本]。
本篇文章介绍"LinkedList"
adlist
adlist.h - A generic doubly linked list implementation
adlist 是一个通用的双向链表
listNode
首先看一下链表节点是如何定义的
typedef struct listNode {struct listNode *prev;//指向前一个节点的指针struct listNode *next;//指向后一个节点的指针void *value;
} listNode;
list
链表的定义
typedef struct list {listNode *head;//头节点listNode *tail;//尾节点/*以下是一些指向"实现特定功能的函数"的指针*/void *(*dup)(void *ptr);//dup指向传入参数类型和返回值类型都是"void*"的函数void (*free)(void *ptr);//dup指向传入参数类型和返回值类型都是"void*"的函数int (*match)(void *ptr, void *key);//dup指向传入参数类型是两个"void *"和返回值类型都是"int*"的函数unsigned long len;//记录list中的元素个数
} list;
- 获取list的元素个数的时间复杂度为O(1)因为链表的定义中包含记录节点个数的字段len
macros[宏定义]
来看看都定义了哪些宏方法。
//获取长度,由此可以看出对于OBJ_ENCODING_LINKEDLIST 编码方式的list来说,获取长度的时间复杂度为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)
//获取节点指向的value值
#define listNodeValue(n) ((n)->value)
listCreate
//创建一个list
list *listCreate(void)
{struct list *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;
}
listInitNode
//初始化链表节点,将前后指针置为空,value值置为对应的value值
void listInitNode(listNode *node, void *value) {node->prev = NULL;node->next = NULL;node->value = value;
}
listEmpty
//清空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;//释放当前节点value指向的内存if (list->free) list->free(current->value);//释放当前节点zfree(current);current = next;}//头尾节点设置为NULL,len设置为0list->head = list->tail = NULL;list->len = 0;
}
listRelease
/* Free the whole list.** This function can't fail. */
void listRelease(list *list)
{listEmpty(list);//释放list本身zfree(list);
}
listAddNodeHead
list *listAddNodeHead(list *list, void *value)
{listNode *node;//为新结点分配内存if ((node = zmalloc(sizeof(*node))) == NULL)return NULL;node->value = value;//从头部插入listlistLinkNodeHead(list, node);return list;
}
listLinkNodeHead
void listLinkNodeHead(list* list, listNode *node) {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;}//元素个数加1list->len++;
}

listAddNodeTail
list *listAddNodeTail(list *list, void *value)
{listNode *node;//为新节点分配内存if ((node = zmalloc(sizeof(*node))) == NULL)return NULL;node->value = value;listLinkNodeTail(list, node);return list;
}
listLinkNodeTail
void listLinkNodeTail(list *list, listNode *node) {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;}//元素个数加1list->len++;
}

listInsertNode
//如果after为1则在old_node之后插入新节点
//反之在old_node之前插入新节点
list *listInsertNode(list *list, listNode *old_node, void *value, int after) {listNode *node;//为node分配内存if ((node = zmalloc(sizeof(*node))) == NULL)return NULL;node->value = value;//在old_node之后插入新节点if (after) {node->prev = old_node;node->next = old_node->next;//如果old_node是尾节点,插入之后要修改tailif (list->tail == old_node) {list->tail = node;}} else {//在old_node之前插入新节点node->next = old_node;node->prev = old_node->prev;//如果old_node是头节点,插入之后要修改headif (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;
}
listDelNode
void listDelNode(list *list, listNode *node)
{listUnlinkNode(list, node);//移除node并释放内存if (list->free) list->free(node->value);zfree(node);
}
listUlinkNode
//从list中移除node
void listUnlinkNode(list *list, listNode *node) {//node->prev不为空说明node不是第一个元素if (node->prev)node->prev->next = node->next;else//node是第一个元素,删除之后需要修改head节点list->head = node->next;//node不是最后一个元素if (node->next)node->next->prev = node->prev;else//node是最后一个元素,删除之后需要修改tail节点list->tail = node->prev;//从list中移除nodenode->next = NULL;node->prev = NULL;//元素个数减1list->len--;
}
listIndex
/*
寻找list中第index个元素,正序遍历index从0开始,0代表head
倒序遍历从-1开始,-1指向tail
*/
listNode *listIndex(list *list, long index) {listNode *n;//如果index<0先将其转换为正数if (index < 0) {//这里减一是因为,从尾节点找到第一个元素无需移动只需返回尾节点即可;找到第二个元素只需从尾节点向前移动一次即可index = (-index)-1;n = list->tail;while(index-- && n) n = n->prev;} else {n = list->head;while(index-- && n) n = n->next;}return n;
}
redis3.2.100quicklist
在redis3.2.100版本中quicklist是由“ziplist”组成的"linkedlist"
quicklist.c - A doubly linked list of ziplists
看一下它的结构
/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist.* We use bit fields keep the quicklistNode at 32 bytes.* count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).* encoding: 2 bits, RAW=1, LZF=2.* container: 2 bits, NONE=1, ZIPLIST=2.* recompress: 1 bit, bool, true if node is temporarry decompressed for usage.* attempted_compress: 1 bit, boolean, used for verifying during testing.* extra: 12 bits, free for future use; pads out the remainder of 32 bits */
/*
quicklistnode是“由ziplist组成的linkedlist”的节点,占有32bytes
*/
typedef struct quicklistNode {struct quicklistNode *prev;struct quicklistNode *next;unsigned char *zl;unsigned int sz; /* ziplist size in bytes ziplist占用的bytes*/unsigned int count : 16; /* count of items in ziplist ziplist里的entry数量*/unsigned int encoding : 2; /* RAW==1 or LZF==2 */unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */unsigned int recompress : 1; /* was this node previous compressed? */unsigned int attempted_compress : 1; /* node can't compress; too small */unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
/* quicklist is a 32 byte struct (on 64-bit systems) describing a quicklist.* 'count' is the number of total entries.* 'len' is the number of quicklist nodes.* 'compress' is: -1 if compression disabled, otherwise it's the number* of quicklistNodes to leave uncompressed at ends of quicklist.* 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count; /* total count of all entries in all ziplists 所有ziplist中的所有entry总和,也就是linkedlist中总的元素个数 */unsigned int len; /* number of quicklistNodes quicklistnode的个数*/int fill : 16; /* fill factor for individual nodes */unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;
redis7.2.2quicklist
在7.2.2版本中quicklist是由“listpack组成的”
quicklist.c - A doubly linked list of listpacks
看一下它的结构
/* quicklistNode is a 32 byte struct describing a listpack for a quicklist.* We use bit fields keep the quicklistNode at 32 bytes.* count: 16 bits, max 65536 (max lp bytes is 65k, so max count actually < 32k).* encoding: 2 bits, RAW=1, LZF=2.* container: 2 bits, PLAIN=1 (a single item as char array), PACKED=2 (listpack with multiple items).* recompress: 1 bit, bool, true if node is temporary decompressed for usage.* attempted_compress: 1 bit, boolean, used for verifying during testing.* extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {struct quicklistNode *prev;struct quicklistNode *next;unsigned char *entry;size_t sz; /* entry size in bytes listpack占用bytes*/unsigned int count : 16; /* count of items in listpack listpack中的元素个数*/unsigned int encoding : 2; /* RAW==1 or LZF==2 */unsigned int container : 2; /* PLAIN==1 or PACKED==2 */unsigned int recompress : 1; /* was this node previous compressed? */unsigned int attempted_compress : 1; /* node can't compress; too small */unsigned int dont_compress : 1; /* prevent compression of entry that will be used later */unsigned int extra : 9; /* more bits to steal for future usage */
} quicklistNode;
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.* 'count' is the number of total entries.* 'len' is the number of quicklist nodes.* 'compress' is: 0 if compression disabled, otherwise it's the number* of quicklistNodes to leave uncompressed at ends of quicklist.* 'fill' is the user-requested (or default) fill factor.* 'bookmarks are an optional feature that is used by realloc this struct,* so that they don't consume memory when not used. */
typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count; /* total count of all entries in all listpacks linkedlist的元素总数*/unsigned long len; /* number of quicklistNodes quicklistnode的数量 */signed int fill : QL_FILL_BITS; /* fill factor for individual nodes */unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */unsigned int bookmark_count: QL_BM_BITS;quicklistBookmark bookmarks[];
} quicklist;
总结
- 不管是ziplist还是listpack构成的quicklist获取元素总数的时间复杂度为都为O(1)。
相关文章:
从[redis:LinkedList]中学习链表
文章目录 adlistlistNodelistmacros[宏定义]listCreatelistInitNodelistEmptylistReleaselistAddNodeHeadlistLinkNodeHeadlistAddNodeTaillistLinkNodeTaillistInsertNodelistDelNodelistUlinkNodelistIndexredis3.2.100quicklistredis7.2.2quicklist redis的基本数据类型之一…...
Prometheus+grafana配置监控系统
使用docker compose安装 方便拓展, 配置信息都放在在 /docker/prometheus 目录下 1.目录结构如下 . ├── conf │ └── prometheus.yml ├── grafana_data ├── prometheus_data └── prometheus_grafana.yaml2.创建目录文件 mkdir /docker/prometheus &&am…...
Linux之安装配置CentOS 7
一、CentOS简介 CentOS(Community Enterprise Operating System,中文意思是社区企业操作系统)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,…...
神经网络与深度学习Pytorch版 Softmax回归 笔记
Softmax回归 目录 Softmax回归 1. 独热编码 2. Softmax回归的网络架构是一个单层的全连接神经网络。 3. Softmax回归模型概述及其在多分类问题中的应用 4. Softmax运算在多分类问题中的应用及其数学原理 5. 小批量样本分类的矢量计算表达式 6. 交叉熵损失函数 7. 模型预…...
git学习及简单maven打包
前提: 已经有远程仓库地址 和账号密码了 已经安装git了 1.本地新建文件夹A用作本地仓库 2.在A文件夹下右键打开GIT BASH HERE 3.创建用户和密码,方便追踪提交记录 git config --global user.email “caoqingqing0108” //创建邮箱 git config --global …...
如何用MapTalks IDE来发布网站?
简介 MapTalks IDE 全称 MapTalks集成设计环境(Integrated Design Environment),是由MapTalks技术团队开发的新一代web地图设计软件。 通过MapTalks IDE,您可以自由的创建二维和三维地图,在其中载入或创建地理数据&a…...
我用selenium开发了一个自动创建任务,解放重复性工作
我用selenium开发了一个自动创建任务,大大解放了我做重复性工作带来的疲惫感,收获了更多的乐趣。 我司有100多个服务,运维忙不过来的时候,就会让我们自己创建云负载,你首先需要在云服务上创建负载,再创建容…...
安卓11修改HDMI自适应分辨率
客户需要hdmi自适应屏幕分辨率,没发现有相关的指令,我发现设置中有个hdmi的Auto选项,于是就试试选中这个选项,试下了可以自适应,于是就找到相关代码,在开机完成后执行这个代码,基本满足需求&…...
Linux实验记录:使用Apache的虚拟主机功能
前言: 本文是一篇关于Linux系统初学者的实验记录。 参考书籍:《Linux就该这么学》 实验环境: VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 正文: 目录 前言: 正文&…...
分布式空间索引了解与扩展
目录 一、空间索引快速理解 (一)区域编码 (二)区域编码检索 (三)Geohash 编码 (四)RTree及其变体 二、业内方案选取 三、分布式空间索引架构 (一)PG数…...
Set和Map的应用场景
Set: 1.成员不能重复 2.只有键值,没有键名,有点类似数组 3.可以遍历,方法 add,delete,has Map: 1.本质上是键值对的集合,类似集合; 2.可以遍历,方法很多,可以干跟各种数据格式转换 Set和…...
小白级教程,10秒开服《幻兽帕鲁》
在帕鲁的世界,你可以选择与神奇的生物「帕鲁」一同享受悠闲的生活,也可以投身于与偷猎者进行生死搏斗的冒险。帕鲁可以进行战斗、繁殖、协助你做农活,也可以为你在工厂工作。你也可以将它们进行售卖,或肢解后食用。 前言 马上过年…...
IDEA 构建开发环境
本博客主要讲解了如何创建一个Maven构建Java项目。(本文是创建一个用Maven构建项目的方式,所以需要对Maven有一定的了解) IDEA 构建开发环境 一、创建一个空工程二、构建一个普通的Maven模块 一、创建一个空工程 创建一个空的工程 * 设置整…...
归并排序----C语言数据结构
目录 引言 1.归并排序的实现----c2.归并排序的复杂度分析时间复杂度空间复杂度 引言 归并排序(Merge Sort) 是一种基于分治法的排序算法,它的基本思想是将原始数组划分成较小的数组,然后递归地对这些小数组进行排序,最后将排好序…...
【网站项目】065健康综合咨询问诊平台
🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板ÿ…...
Adobe Camera Raw forMac/win:掌控原始之美的秘密武器
Adobe Camera Raw,这款由Adobe开发的插件,已经成为摄影师和设计师们的必备工具。对于那些追求完美、渴望探索更多创意可能性的专业人士来说,它不仅仅是一个插件,更是一个能够释放无尽创造力的平台。 在数字摄影时代,R…...
OpenHarmony—开发及引用静态共享包(API 9)
HAR(Harmony Archive)是静态共享包,可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP,不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。 接下来&a…...
测试面试题常见题
文章目录 功能测试一个完整的测试计划应该包含哪些内容一个完整的测试用例包含哪些内容?什么时候需要发测试报告?一份测试报告应该包含哪些内容?一个完整的缺陷报告应该包含哪些内容?简述等价类划分法并举例针对具体场景的测试用例…...
代码随想录算法训练营第六天 - 哈希表part02
454.四数之和II 核心思想:利用字典的key,value 4个数组两两分组,nums1nums2 的两两元素之和 及 计数 先存入字典中,然后对nums3和nums4的进行元素相加 然后对比字典中是否有对应的key,有就countvalue class Solution…...
【Javaweb程序设计】【C00165】基于SSM的高考志愿辅助填报系统(论文+PPT)
基于SSM的高考志愿辅助填报系统(论文PPT) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的高考志愿辅助填报系统 本系统分为前台系统模块、后台管理员模块以及后台学生模块 前台系统模块:当游客打开系统的网址后&…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
