从[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.hqwc.cn/news/453804.html

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

相关文章

day 19 (进阶)

一 首先 昨日内容回顾 思维导图&#xff1a;&#xff08;日更附 养成习惯 加油&#xff09; 补充Linux思维导图 衔接一下之前学过的 二 课堂知识提炼 练习&#xff1a;统计文件行数 想查看是否正确就用 grep -c “文件名” 来看 会输出结果 练习&#xff1a;把file.c里面的…

JS第二天、原型、原型链、正则

☆☆☆☆ 什么是原型&#xff1f; 构造函数的prototype 就是原型 专门保存所有子对象共有属性和方法的对象一个对象的原型就是它的构造函数的prototype属性的值。prototype是哪来的&#xff1f;所有的函数都有一个prototype属性当函数被创建的时候&#xff0c;prototype属性…

从资深用户角度谈三款出色数据可视化工具

作为一名数据可视化领域的老用户&#xff0c;我接触过众多数据可视化产品&#xff0c;其中不乏佼佼者。今天&#xff0c;我想为大家介绍三款在我心目中颇具特色的数据可视化产品&#xff0c;它们分别是山海鲸可视化、Tableau和Power BI。 首先&#xff0c;让我们来谈谈山海鲸可…

Python字符串和日期时间格式转换

Python字符串和日期时间格式转换 前言&#xff1a;1.字符串和日期时间转换终论&#xff1a;给定月份的上月月份(YYYYMM)1.1格式YYYYMM变成YYYYMMDD1.2字符串转换为时间格式1.3时间格式加减1.4时间格式转换为字符串 2.Pandas的DataFrame时间格式转换 前言&#xff1a; 字符串转…

vulhub中Apache APISIX Dashboard API权限绕过导致RCE(CVE-2021-45232)

Apache APISIX是一个动态、实时、高性能API网关&#xff0c;而Apache APISIX Dashboard是一个配套的前端面板。 Apache APISIX Dashboard 2.10.1版本前存在两个API/apisix/admin/migrate/export和/apisix/admin/migrate/import&#xff0c;他们没有经过droplet框架的权限验证&…

精细管理药厂设备,制药机械设备管理平台系统助力生产提效

制药行业的复杂性要求对药品的品质和安全性进行严格控制&#xff0c;而这离不开高效管理各类机械设备。然而&#xff0c;随着制药企业规模的不断扩大和技术的迅猛进步&#xff0c;如何有效管理这些设备成为一个亟待解决的问题。在这一挑战面前&#xff0c;PreMaint制药机械设备…

【C++基础入门】六、函数(定义、调用、声明、值传递、有参无参有反无反、分文件编写)

六、函数 6.1 概述 作用&#xff1a; 将一段经常使用的代码封装起来&#xff0c;减少重复代码 一个较大的程序&#xff0c;一般分为若干个程序块&#xff0c;每个模块实现特定的功能。 6.2 函数的定义 函数的定义一般主要有5个步骤&#xff1a; 1、返回值类型 2、函数名…

C++一维数组

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 铁汁们大家好呀&#xff0c;我是PingdiGuo_guo&#xff0c;今天我们来学习一下数组&#xff08;一维&#xff09;。 文章目录 1.数组的概念与思想 2.为什么要使用数组 3.数组的特性 4.数组的操作 1.定义…

普渡机器人CEO预测2024年服务机器人市场将扩大

原创 | 文 BFT机器人 根据普渡科技有限公司的报告&#xff0c;商用服务机器人在东亚地区的应用比其他地方更为广泛。然而&#xff0c;预计到2024年&#xff0c;全球其他地区也将迎头赶上。这家总部位于中国深圳的公司自豪地宣称&#xff0c;它已经成为中国最大的此类机器人出口…

Docker 可视化工具

1、Portainer 概念介绍 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环境和集群环境。 Portainer分为开源社区版&#xff08;CE版&#xff09;和商用版&#xff08;BE版/EE版&#xff09;。 Porta…

【方法】如何把PPT的“只读方式”变成可直接编辑?

PPT文件如果设置了以“只读方式”打开&#xff0c;就会无法编辑&#xff0c;那如何才能变成可直接编辑呢&#xff1f;下面我们分两种情况来具体说说&#xff0c;不清楚的小伙伴一起来看看吧。 情况一&#xff1a; PPT设置的是无密码的“只读方式”&#xff0c;这种方式下&…

One time pad 图像加密MATLAB程序

使用一次加密的形式对图像进行加密。 采用异或的方式实现。 加密、解密结果如下: 程序代码如下: % 读取原始图像并显示 originalImage = imread(lena256.bmp); % 更换为你的图像文件名 subplot(1,3,1),imshow(originalImage); title(Original Image);% 生成与图像相同大…