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

从[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&#xff08;Community Enterprise Operating System&#xff0c;中文意思是社区企业操作系统&#xff09;是Linux发行版之一&#xff0c;它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码&#xff0c…...

神经网络与深度学习Pytorch版 Softmax回归 笔记

Softmax回归 目录 Softmax回归 1. 独热编码 2. Softmax回归的网络架构是一个单层的全连接神经网络。 3. Softmax回归模型概述及其在多分类问题中的应用 4. Softmax运算在多分类问题中的应用及其数学原理 5. 小批量样本分类的矢量计算表达式 6. 交叉熵损失函数 7. 模型预…...

git学习及简单maven打包

前提&#xff1a; 已经有远程仓库地址 和账号密码了 已经安装git了 1.本地新建文件夹A用作本地仓库 2.在A文件夹下右键打开GIT BASH HERE 3.创建用户和密码&#xff0c;方便追踪提交记录 git config --global user.email “caoqingqing0108” //创建邮箱 git config --global …...

如何用MapTalks IDE来发布网站?

简介 MapTalks IDE 全称 MapTalks集成设计环境&#xff08;Integrated Design Environment&#xff09;&#xff0c;是由MapTalks技术团队开发的新一代web地图设计软件。 通过MapTalks IDE&#xff0c;您可以自由的创建二维和三维地图&#xff0c;在其中载入或创建地理数据&a…...

我用selenium开发了一个自动创建任务,解放重复性工作

我用selenium开发了一个自动创建任务&#xff0c;大大解放了我做重复性工作带来的疲惫感&#xff0c;收获了更多的乐趣。 我司有100多个服务&#xff0c;运维忙不过来的时候&#xff0c;就会让我们自己创建云负载&#xff0c;你首先需要在云服务上创建负载&#xff0c;再创建容…...

安卓11修改HDMI自适应分辨率

客户需要hdmi自适应屏幕分辨率&#xff0c;没发现有相关的指令&#xff0c;我发现设置中有个hdmi的Auto选项&#xff0c;于是就试试选中这个选项&#xff0c;试下了可以自适应&#xff0c;于是就找到相关代码&#xff0c;在开机完成后执行这个代码&#xff0c;基本满足需求&…...

Linux实验记录:使用Apache的虚拟主机功能

前言&#xff1a; 本文是一篇关于Linux系统初学者的实验记录。 参考书籍&#xff1a;《Linux就该这么学》 实验环境&#xff1a; VmwareWorkStation 17——虚拟机软件 RedHatEnterpriseLinux[RHEL]8——红帽操作系统 正文&#xff1a; 目录 前言&#xff1a; 正文&…...

分布式空间索引了解与扩展

目录 一、空间索引快速理解 &#xff08;一&#xff09;区域编码 &#xff08;二&#xff09;区域编码检索 &#xff08;三&#xff09;Geohash 编码 &#xff08;四&#xff09;RTree及其变体 二、业内方案选取 三、分布式空间索引架构 &#xff08;一&#xff09;PG数…...

Set和Map的应用场景

Set: 1.成员不能重复 2.只有键值&#xff0c;没有键名&#xff0c;有点类似数组 3.可以遍历&#xff0c;方法 add,delete,has Map: 1.本质上是键值对的集合&#xff0c;类似集合&#xff1b; 2.可以遍历&#xff0c;方法很多&#xff0c;可以干跟各种数据格式转换 Set和…...

小白级教程,10秒开服《幻兽帕鲁》

在帕鲁的世界&#xff0c;你可以选择与神奇的生物「帕鲁」一同享受悠闲的生活&#xff0c;也可以投身于与偷猎者进行生死搏斗的冒险。帕鲁可以进行战斗、繁殖、协助你做农活&#xff0c;也可以为你在工厂工作。你也可以将它们进行售卖&#xff0c;或肢解后食用。 前言 马上过年…...

IDEA 构建开发环境

本博客主要讲解了如何创建一个Maven构建Java项目。&#xff08;本文是创建一个用Maven构建项目的方式&#xff0c;所以需要对Maven有一定的了解&#xff09; IDEA 构建开发环境 一、创建一个空工程二、构建一个普通的Maven模块 一、创建一个空工程 创建一个空的工程 * 设置整…...

归并排序----C语言数据结构

目录 引言 1.归并排序的实现----c2.归并排序的复杂度分析时间复杂度空间复杂度 引言 归并排序&#xff08;Merge Sort) 是一种基于分治法的排序算法&#xff0c;它的基本思想是将原始数组划分成较小的数组&#xff0c;然后递归地对这些小数组进行排序&#xff0c;最后将排好序…...

【网站项目】065健康综合咨询问诊平台

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…...

Adobe Camera Raw forMac/win:掌控原始之美的秘密武器

Adobe Camera Raw&#xff0c;这款由Adobe开发的插件&#xff0c;已经成为摄影师和设计师们的必备工具。对于那些追求完美、渴望探索更多创意可能性的专业人士来说&#xff0c;它不仅仅是一个插件&#xff0c;更是一个能够释放无尽创造力的平台。 在数字摄影时代&#xff0c;R…...

OpenHarmony—开发及引用静态共享包(API 9)

HAR(Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP&#xff0c;不能独立安装运行在设备上&#xff0c;只能作为应用模块的依赖项被引用。 接下来&a…...

测试面试题常见题

文章目录 功能测试一个完整的测试计划应该包含哪些内容一个完整的测试用例包含哪些内容&#xff1f;什么时候需要发测试报告&#xff1f;一份测试报告应该包含哪些内容&#xff1f;一个完整的缺陷报告应该包含哪些内容&#xff1f;简述等价类划分法并举例针对具体场景的测试用例…...

代码随想录算法训练营第六天 - 哈希表part02

454.四数之和II 核心思想&#xff1a;利用字典的key&#xff0c;value 4个数组两两分组&#xff0c;nums1nums2 的两两元素之和 及 计数 先存入字典中&#xff0c;然后对nums3和nums4的进行元素相加 然后对比字典中是否有对应的key&#xff0c;有就countvalue class Solution…...

【Javaweb程序设计】【C00165】基于SSM的高考志愿辅助填报系统(论文+PPT)

基于SSM的高考志愿辅助填报系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的高考志愿辅助填报系统 本系统分为前台系统模块、后台管理员模块以及后台学生模块 前台系统模块&#xff1a;当游客打开系统的网址后&…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

让AI看见世界:MCP协议与服务器的工作原理

让AI看见世界&#xff1a;MCP协议与服务器的工作原理 MCP&#xff08;Model Context Protocol&#xff09;是一种创新的通信协议&#xff0c;旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天&#xff0c;MCP正成为连接AI与现实世界的重要桥梁。…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...