Redis源码阅读—数据结构之双端链表 adlist.h/adlist.c

adlist.h/adlist.c


一、 adlist 的定义

由于 C 语言没有内置的链表这种常用的数据结构,因此 Redis 实现了自己的链表实现。

Redis 对链表的实现,包括链表( list )、链表节点( listNode )以及链表迭代器( listIter )三个数据结构。

其中,链表节点在 adlist.h/listNode 中进行了定义:


//链表节点
typedef struct listNode {

    // 前置节点
    struct listNode *prev;
    
    // 后置节点
    struct listNode *next;
    
    // 节点的值
    void *value;
    
} listNode;

多个链表节点通过 prve 和 next 指针组成双端链表,如下图所示:

由多个链表节点组成的双端链表

在链表节点组成双端链表后,由链表( adlist.h/list )来持有双端链表的话,操作起来会更加方便。

链表在 adlist.h/list 中进行了定义:


//链表
typedef struct list {

    // 表头节点
    listNode *head;
    
    // 表尾节点
    listNode *tail;
    
    // 链表长度,即链表所包含的节点数量
    unsigned long len;
    
    // 节点值复制函数
    void *(*dup)(void *ptr);
    
    // 节点值释放函数
    void (*free)(void *ptr);
    
    // 节点值对比函数
    int (*match)(void *ptr, void *key);
    
} list;

下图是一个 list 结构与三个 listNode 结构组成的链表:

由 list 结构与 listNode 结构组成的链表

迭代器在 adlist.h/listIter 中进行了定义:


//迭代器
typedef struct listIter {

    // 当前迭代到的节点
    listNode *next;
    
    // 迭代的方向
    // 0 表示从表头向表尾进行迭代
    // 1 表示从表尾向表头进行迭代
    int direction;  
    
} listIter;

//迭代器进行迭代的方向
 
// 从表头向表尾进行迭代
#define AL_START_HEAD 0

// 从表尾到表头进行迭代
#define AL_START_TAIL 1


二、 adlist 的 API

adlist 的函数实现分为两种,一种是通过宏定义的函数,一种是普通函数。

  1. 宏定义函数
函数 作用 时间复杂度
listLength 返回给定链表的长度,即所包含的节点个数 O(1)
listFirst 返回给定链表的表头节点 O(1)
listLast 返回给定链表的表尾节点 O(1)
listPrevNode 返回给定链表节点的前一个结点 O(1)
listNextNode 返回给定链表节点的后一个结点 O(1)
listNodeValue 返回给定链表节点的值 O(1)
listSetDupMethod 将给定的函数设置为给定链表的节点值复制函数 O(1)
listSetFreeMethod 将给定的函数设置为给定链表的节点值释放函数 O(1)
listSetMatchMethod 将给定的函数设置为给定链表的节点值对比函数 O(1)
listGetDupMethod 返回给定链表当前正在使用的节点值复制函数 O(1)
listGetFree 返回给定链表当前正在使用的节点值释放函数 O(1)
listGetMatchMethod 返回给定链表当前正在使用的节点值对比函数 O(1)
  1. 普通函数
函数 作用 时间复杂度
listCreate 创建一个不包含任何节点的空链表 O(1)
listRelease 释放给定链表,包括链表中的所有节点 O(N),N为链表中的节点数
listAddNodeHead 将包含给定值的新节点插入到给定链表的表头 O(1)
listAddNodeTail 将包含给定值的新节点插入到给定链表的表尾 O(1)
listInsertNode 将包含给定值的新节点插入到给定节点的之前或之后 O(1)
listDelNode 从给定链表中删除给定节点 O(1)
listGetIterator 为给定链表创建一个迭代器,并为其配置迭代方向 O(1)
listNext 返回迭代器当前所指向的链表节点 O(1)
listReleaseIterator 释放给定迭代器 O(1)
listDup 复制给定链表,并返回复制后的副本 O(N),N为链表中的节点数
listSearchKey 查找并返回链表中包含给定值的结点,查找方向为从头至尾 O(N),N为链表中的节点数
listIndex 查找并返回链表中在给定索引上的结点,索引值从零开始,可以为负(索引为负表示从表尾开始查找,-1 表示表尾节点,-2 表示倒数第二个结点) O(N),N为链表中的节点数
listRewind 将给定迭代器的方向设置为从头到尾(AL_START_HEAD),并将迭代指针重新指向表头节点 O(1)
listRewindTail 将给定迭代器的方向设置从尾到头(AL_START_TAIL),并将迭代指针重新指向表尾节点 O(1)
listRotate 将链表的表尾结点弹出,并插入到链表的表头,成为新的表头结点 O(1)

三、 adlist 的特性

  1. 双端链表
    链表节点有 prevnext 指针,所以获取某节点的前后结点的复杂度都为O(1)。

  2. 无环
    链表无环,即链表的表头结点的 prev 指针和表尾节点的 next 指针都指向 NULL ,所以在通过迭代器对链表进行访问时(无论方向),都会以 NULL 为结尾,不会出现循环。

  3. 带有表头结点和表尾结点
    list 结构带有 headtail 指针,所以获取某链表的头尾结点的复杂度都为O(1)。

  4. 带有链表长度
    list 结构带有 len 属性,所以获取某链表的长度的复杂度仅为O(1)。

  5. 多态
    链表节点使用 void * 指针来持有节点值,所以链表可以用于保存各种类型的值。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容