从[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)。

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

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

相关文章

简单说说mysql的日志

今天我们通过mysql日志了解mysqld的错误日志、慢查询日志、二进制日志&#xff0c;redolog, undolog等。揭示它们的作用和用途&#xff0c;让我们工作中更能驾驭mysql。 redo 日志 如果mysql事务提交后发生了宕机现象&#xff0c;那怎么保证数据的持久性与完整性&#xff1f;…

网站接入QQ登录

开发者认证&#xff0c;创建应用&#xff1a; &#xff08;1&#xff09;先在 QQ 互联 QQ互联管理中心 进行个人开发者认证&#xff0c;并审核 &#xff08;2&#xff09;创建【网站应用】&#xff0c;填写正确信息&#xff0c;获得对应的 appid 与 appkey&#xff0c;并审核…

【机器学习】某闯关类手游用户流失预测

Final Project: 某闯关类手游用户流失预测 1 案例简介 手游在当下的日常娱乐中占据着主导性地位&#xff0c;成为人们生活中放松身心的一种有效途径。近年来&#xff0c;各种类型的手游&#xff0c;尤其是闯关类的休闲手游&#xff0c;由于其对碎片化时间的利用取得了非常广泛…

服务器未启动而端口进程仍在运行如何查看并杀死

首先登录服务器然后查看当前监听的端口&#xff1a; sudo netstat -tuln比如这里的8080&#xff0c;我们此时并未启动服务器&#xff0c;但是它却正在运行&#xff0c;这会导致服务器刚启动就秒挂。如果没有日志的话会让人有点疑惑&#xff0c;这种情况可能是之前运行了该进程…

代码随想录day17--二叉树的应用5

LeetCode654.最大二叉树 题目描述&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后…

鸿蒙开发-UI-页面路由

鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 文章目录 一、基本概念 二、页面跳转 1.router基本概念 2.使用场景 3.页面跳转参数传递 三、页面返回 1.普通页面返回 2.页面返回前增加一个询问框 1.系统默认询问框 2.自定义询问框 总…

EtherCAT FP介绍系列文章—UDP gateway

EtherCAT主站上的Mailbox Gateway功能&#xff0c;可以用于将EtherCAT mailbox相关协议从外部设备的工具通过邮箱网关路由到EtherCAT从站设备。在EtherCAT规范中定义的所有邮箱协议在此功能中都可用&#xff0c;例如CoE, FoE, VoE, SoE。 但是&#xff0c;这里特别注意的是Mai…

雨云游戏云VPS服务器用Linux搭建MCSM面板和Minecraft Mohist 1.20.2服务器教程,我的世界MOD和插件服开服教程。

雨云游戏云VPS服务器用Linux搭建MCSM面板和Minecraft Mohist 1.20.2服务器教程&#xff0c;我的世界MOD和插件服开服教程。 本教程演示安装的MC服是Mohist 1.20.2版&#xff0c;其他版本也可以参考本教程&#xff0c;差别不大。 本教程使用Docker来运行mc服&#xff0c;可以方…

UserWarning: Glyph 39640 missing from current font问题

是因为不支持中文字体导致的&#xff0c;设置为一个支持中文的字体就行了。 另外&#xff0c;上面的改动会引起负号显示为方块&#xff0c;需要额外再加一条设置。 在中文系统上 import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 设置为一个支持…

C#,斯特林数(Stirling Number)的算法与源代码

1 斯特林数 在组合数学&#xff0c;斯特林数可指两类数&#xff0c;第一类斯特林数和第二类斯特林数&#xff0c;都是由18世纪数学家James Stirling提出的。它们自18世纪以来一直吸引许多数学家的兴趣&#xff0c;如欧拉、柯西、西尔沃斯特和凯莱等。后来哥本哈根&#xff08;…

Docker容器引擎私有仓库的搭建和管理

目录 一、私有仓库搭建和管理 &#xff08;一&#xff09;搭建本地私有仓库 1.拉取registry镜像 2.修改docker配置文件并重启 3.运行registry容器 4.给镜像打标签 5.上传到私有仓库 6.查看私有仓库的所有镜像 7.从私有仓库拉取镜像 &#xff08;1&#xff09;删除cen…

网络协议 UDP协议

网络协议 UDP协议 在之前的文章中有对UDP协议套接字的使用进行讲解&#xff0c;本文主要对UDP协议进行一些理论补充。 文章目录 网络协议 UDP协议1. 概念2. UDP协议格式2.1 数据报长度2.2 校验和/检验和2.2.1 CRC校验2.2.2 MD5算法 1. 概念 UDP&#xff0c;即User Datagram P…

gitlab操作手册

git操作篇 1. 项目克隆 git clone gitgitlab.test.cn:pro/project1.git2. 项目的提交 注&#xff1a;如果要查看文件的状态可以用git status命令&#xff1a; 如上图所示&#xff0c;文件已经修改了。 3. 项目的推送 git push origin feature/test01注&#xff1a;如果要查…

nodejs+vue+ElementUi宠物领养救助网站0w6wc

宠物领养救助平台采用B/S架构&#xff0c;数据库是MySQL。网站的搭建与开发采用了先进的nodejs进行编写&#xff0c;使用了vue框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。主要功能包括&#xff1a;个人信息修改&#xff0c;对用户、宠物类别、宠物…

深信服技术认证“SCSA-S”划重点:安全事件管理处置

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信…

MySQL-运维-主从复制

一、概述 二、原理 三、搭建 1、服务器准备 2、主库配置 &#xff08;1&#xff09;、修改配置文件/etc/my.cnf &#xff08;2&#xff09;、重启MySQL服务器 &#xff08;3&#xff09;、登录mysql&#xff0c;创建远程链接的账号&#xff0c;并授予主从复制权限 &#xff0…

shell脚本-免交互

一、Here Document免交互&#xff1a; 1.交互概述&#xff1a; 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编程人员可以发出指令控制该程序的运行&#xff0c;而不是程序单方面执行下去&#xff0c;程序在接受到编程人员相应的指令后而相应地做出反应。 对于…

铁轨语义分割(Unet结合resnet系列)

数据介绍 一类是图片&#xff0c;一类是图像标签。 引入库&#xff0c;处理数据 import torch.nn as nn import torch import torch.nn.functional as F import os from PIL import Image import torch from torch.utils.data import Dataset import torchvision.transfor…

Unity SRP 管线【第七讲:URP LOD实现以及Reflections反射探针】

目录 一、URP LOD 组件1、LOD Group的使用2、LOD切换原理Cross Fade(淡入淡出)模式Animated Cross-Fading如果未设置Clip&#xff0c;并且Fade Transition Width不为0LOD物体烘培 SpeedTree 模式 二、反射探针1. 获取反射探针数据2. 环境光照明 IBL3. 反射探针&#xff08;Refl…

部署前后端分离项目详细教程

部署前后端分离项目详细教程 1、准备工作 首先你需要一台服务器&#xff0c;然后在服务器上安装好你所需要的环境&#xff0c;我这里用的宝塔界面来安装环境。 如果有人不知道怎么安装宝塔界面&#xff0c;可参考这篇文章&#xff0c;如果不知道怎么买服务器&#xff0c;可以参…