侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Redis中的双端链表实现

Redis  /  管理员 发布于 7年前   267

adlist作为Redis中的双端链表,在Redis中被广泛的应用到了很多地方,比如 slowlog的存储,主从复制中报错客户端,list数据结构的实现等,很多都与此相关,所以也是非常重要的一个数据结构。

一)、Redis中双端链表的数据结构

(推荐:redis视频教程)

双端链表(以下代码定义在 src/adlist.h):

typedef struct list {    listNode *head;    //指向链表的第一个节点    listNode *tail;    //指向链表的最后一个节点    //复制链表节点时候的回调函数,由于链表节点可以任意类型的数据,不同类型操作不同,故而用回调函数去特殊处理    void *(*dup)(void *ptr);    void (*free)(void *ptr);     //释放链表节点内存时候的回调函数,理由同上    int (*match)(void *ptr, void *key);    //比较链表节点值的回调函数,用于自定义比较,理由同上    unsigned long len;  //当前列表节点数量} list;

双端链表的节点(以下代码定义在 src/adlist.h):

typedef struct listNode {    struct listNode *prev;   //当前节点的上一个节点    struct listNode *next;   //当前节点的下一个节点    void *value;     //当前节点存储的值,可以任意类型} listNode;

1.jpg

list 通过 head 和 tail两个指针分别指向链表的头尾两端,使得遍历链表可以从正反两个顺序进行遍历,而 listNode 的void *value,则可以保存任意数据,并可以通过dup,free,match来自定义如何处理listNode的值。

二、双端链表的简单操作

创建链表(以下代码定义在 src/adlist.c):

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

追加到链表结尾(以下代码定义在 src/adlist.c):

list *listAddNodeTail(list *list, void *value){    listNode *node;    //初始化node节点    //为新的node节点分配内存    if ((node = zmalloc(sizeof(*node))) == NULL)        return NULL;    //为node节点设置值    node->value = value;        if (list->len == 0) {        //如果空链表则 将头尾指向 新节点 并后继前驱节点设置为NULL        list->head = list->tail = node;        node->prev = node->next = NULL;    } else {        //否则将节点的前驱节点设置为原来的尾部节点        node->prev = list->tail;        //由于追加到尾部,后继节点为NULL        node->next = NULL;        //之前的尾部节点的后继节点设置为新增的节点        list->tail->next = node;        //将列表的尾部节点指针指向新增的节点        list->tail = node;    }    //增加链表长度    list->len++;    return list;}

遍历链表:

常用的遍历链表有两种方式,一个是根据链表长度通过while循环去手动遍历,还有一种是用Redis双端链表提供的迭代器来遍历。

手动遍历(以下代码定义在 src/adlist.c 中的 内存释放函数):

void listRelease(list *list){    unsigned long len;    //current表示当前遍历的游标指针,next表示下次遍历的游标指针(next作为临时变量用于保存下一个节点)    listNode *current, *next;    //将current指向头部节点    current = list->head;    //计算长度(其实就是 listLength(list))    len = list->len;    //通过长度递减的方式进行遍历,便利到长度为0的时候,遍历结束    while(len--) {        //保存下次循环的节点指针        next = current->next;        //释放掉当前节点,如果设置释放节点的回调函数,则执行用户自定义的函数        if (list->free) list->free(current->value);        zfree(current);        //将游标指向下个节点        current = next;    }    zfree(list);}

迭代器方式遍历请见下文。

三)、双端链表的迭代器

Redis 为了方便对链表的迭代,对链表进行了迭代器的封装,迭代器结构如下(以下代码定义在 src/adlist.h):

typedef struct listIter {    listNode *next;    //迭代器指向的下一个节点    //迭代方向,由于双端链表保存了head和tail的值,所以可以进行两个方向的迭代    //AL_START_HEAD表示从头向后遍历,AL_START_TAIL表示从尾部向前遍历    int direction;} listIter;

迭代器使用实例:

//l表示list结构,n表示listNode结构,listNode的值保存的是sds字符串//创建迭代器对象iter = listGetIterator(l, AL_START_HEAD);//循环获取下一个需要遍历的节点while ((n = listNext(iter))) {    //输出返回的节点n的value值    printf("Node Value: %s\n", listNodeValue(n));}

更多redis知识请关注redis入门教程栏目。

以上就是Redis中的双端链表实现的详细内容,更多请关注其它相关文章!


  • 上一条:
    Redis缓存失效机制介绍
    下一条:
    pomelo连接redis的方法介绍
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在Redis中能实现的功能、常见应用介绍(0个评论)
    • 2024年Redis面试题之一(0个评论)
    • 在redis缓存常见出错及解决方案(0个评论)
    • 在redis中三种特殊数据类型:地理位置、基数(cardinality)估计、位图(Bitmap)使用场景介绍浅析(2个评论)
    • Redis 删除 key用 del 和 unlink 有啥区别?(1个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2017-12
    • 2020-03
    • 2020-05
    • 2021-04
    • 2022-03
    • 2022-05
    • 2022-08
    • 2023-02
    • 2023-04
    • 2023-07
    • 2024-01
    • 2024-02
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客