专栏首页CSDN搜“看,未来”【redis6.0.6】redis源码慢慢学,慢慢看 -- 第五天:adlist

【redis6.0.6】redis源码慢慢学,慢慢看 -- 第五天:adlist

前言:

先附上人家的版权:

Copyright © 2009 Salvatore Sanfilippo This file is released under the BSD license, see the COPYING file

考试差不多考完了,我又回来了。 今天起,我们就进入到redis的数据结构模块。 其实吧,这些数据结构我们都写过的,不过看看大佬们写的,也是能收获很多东西的。

先来最基础的链表,它这个是双端链表。

第一次写,有点紧张,我也不知道要怎么排版,嗯,多包涵。

头文件

那就先来个头文件吧。

//结构体:
/*
listNode:节点结构体
listIter:迭代器结构体
list:链表结构体

我也不知道为啥要有这么多结构体,我平时吧,也就俩结构体,一个节点信息,一个数据信息,放这里看,就是那个listNode里面了
不急,看到后面的函数实现就知道了。

字面意思我就不注释了啊,看着乱糟糟
*/

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;	//通用链表
} listNode;

typedef struct listIter {
    listNode *next;	//迭代器当前指向的节点,起了这么个好名字
    int direction;	//可以取以下两个值:AL_START_HEAD和AL_START_TAIL
} listIter;

typedef struct list {
    listNode *head;
    listNode *tail;

	//这种写法应该不陌生吧,总有人吹牛说用结构体实现类的功能,进去一看就是这个,我时间、流量都花了,就给我看这个?
    void *(*dup)(void *ptr);			//复制链表节点保存的值,dup和dup2陌生不?
    void (*free)(void *ptr);	
    int (*match)(void *ptr, void *key);	//比较链表节点所保存的节点值和另一个输入的值是否相等
    unsigned long len;
} list;

//函数宏
#define listLength(l) ((l)->len)	//返回链表l节点数量
#define listFirst(l) ((l)->head)	//后面都懂吧:懂得懂得
#define listLast(l) ((l)->tail)
#define listPrevNode(n) ((n)->prev)
#define listNextNode(n) ((n)->next)
#define listNodeValue(n) ((n)->value)

#define listSetDupMethod(l,m) ((l)->dup = (m))		//设置链表l的复制函数为m方法
#define listSetFreeMethod(l,m) ((l)->free = (m))	
#define listSetMatchMethod(l,m) ((l)->match = (m))	

#define listGetDupMethod(l) ((l)->dup)		//返回链表l的赋值函数
#define listGetFreeMethod(l) ((l)->free)
#define listGetMatchMethod(l) ((l)->match)

/* Prototypes */
list *listCreate(void);
void listRelease(list *list);
void listEmpty(list *list);
list *listAddNodeHead(list *list, void *value);
list *listAddNodeTail(list *list, void *value);
list *listInsertNode(list *list, listNode *old_node, void *value, int after);	//在list中,根据after在old_node节点前后插入值为value的节点。
void listDelNode(list *list, listNode *node);
listIter *listGetIterator(list *list, int direction);	//为list创建一个迭代器iterator
listNode *listNext(listIter *iter);		//返回迭代器iter指向的当前节点并更新iter
void listReleaseIterator(listIter *iter);	
list *listDup(list *orig);	//拷贝表头为orig的链表并返回
listNode *listSearchKey(list *list, void *key);
listNode *listIndex(list *list, long index);	//返回下标为index的节点地址
void listRewind(list *list, listIter *li);		//将迭代器li重置为list的头结点并且设置为正向迭代
void listRewindTail(list *list, listIter *li);	//将迭代器li重置为list的尾结点并且设置为反向迭代
void listRotateTailToHead(list *list);			//将尾节点插到头结点
void listRotateHeadToTail(list *list);
void listJoin(list *l, list *o);		//把o接在l后面

/* Directions for iterators */
#define AL_START_HEAD 0     //正向迭代:从表头向表尾进行迭代
#define AL_START_TAIL 1     //反向迭代:从表尾到表头进行迭代

源文件

关于空间配置器的问题,在这里:【redis6.0.6】redis源码慢慢学,慢慢看 – 第二天:空间配置(zmalloc)

list *listCreate(void);

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

看这个没什么问题吧,大家都这么写。


void listEmpty(list *list);

清空链表,但是不删掉

void listEmpty(list *list)
{
    unsigned long len;
    listNode *current, *next;	//不知道为什么还要特地去做个结构体

    current = list->head;   //备份头节点地址
    len = list->len;        //备份链表元素个数,使用备份操作防止更改原有信息
    while(len--) {          //遍历链表
        next = current->next;
        if (list->free) list->free(current->value); //如果设置了list结构的释放函数,则调用该函数释放节点值
        zfree(current);
        current = next;
    }
    list->head = list->tail = NULL;
    list->len = 0;
}

void listRelease(list *list);

想了想,还是“斩草除根”吧。

void listRelease(list *list)    //释放list表头和链表
{
	listEmpty(list);
    zfree(list);
}

list *listAddNodeHead(list *list, void *value);

list *listAddNodeHead(list *list, void *value)  //将value添加到list链表的头部
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)    //为新节点分配空间
        return NULL;
    node->value = value;    //设置node的value值

    if (list->len == 0) {   //将node头插到空链表
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {                //将node头插到非空链表
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }

    list->len++;    //链表元素计数器加1

    return list;
}

没什么好说的啊


list *listAddNodeTail(list *list, void *value);

list *listAddNodeTail(list *list, void *value)  //将value添加到list链表的尾部
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)    //为新节点分配空间
        return NULL;
    node->value = value;    //设置node的value值
    if (list->len == 0) {   //将node尾插到空链表
        list->head = list->tail = node;
        node->prev = node->next = NULL;
    } else {                //将node头插到非空链表
        node->prev = list->tail;
        node->next = NULL;
        list->tail->next = node;
        list->tail = node;
    }
    list->len++;    //更新链表节点计数器

    return list;
}

list *listInsertNode(list *list, listNode *old_node, void *value, int after);

list *listInsertNode(list *list, listNode *old_node, void *value, int after)    //在list中,根据after在old_node节点前后插入值为value的节点。
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL) //为新节点分配空间
        return NULL;
    node->value = value;    //设置node的value值

    if (after) {    //after 非零,则将节点插入到old_node的后面
        node->prev = old_node;
        node->next = old_node->next;
        if (list->tail == old_node) {   //目标节点如果是链表的尾节点,更新list的tail指针
            list->tail = node;
        }
    } else {        //after 为零,则将节点插入到old_node的前面
        node->next = old_node;
        node->prev = old_node->prev;
        if (list->head == old_node) {   //如果节点如果是链表的头节点,更新list的head指针
            list->head = node;
        }
    }
    if (node->prev != NULL) {   //如果有,则更新node的前驱节点的指针
        node->prev->next = node;
    }
    if (node->next != NULL) {   //如果有,则更新node的后继节点的指针
        node->next->prev = node;
    }
    list->len++;    //更新链表节点计数器
    return list;
}

void listDelNode(list *list, listNode *node);

void listDelNode(list *list, listNode *node)    //从list删除node节点
{
    if (node->prev) //更新node的前驱节点的指针
        node->prev->next = node->next;
    else
        list->head = node->next;
    if (node->next) //更新node的后继节点的指针
        node->next->prev = node->prev;
    else
        list->tail = node->prev;

    if (list->free) list->free(node->value);    //如果设置了list结构的释放函数,则调用该函数释放节点值
    zfree(node);    //释放节点
    list->len--;    //更新链表节点计数器
}

咱也不知道该说点啥,咱自个儿也是这么写的。


listIter *listGetIterator(list *list, int direction);

listIter *listGetIterator(list *list, int direction)    //为list创建一个迭代器iterator
{
    listIter *iter;

    if ((iter = zmalloc(sizeof(*iter))) == NULL) return NULL;   //为迭代器申请空间
    if (direction == AL_START_HEAD)     //设置迭代指针的起始位置
        iter->next = list->head;
    else
        iter->next = list->tail;
    iter->direction = direction;        //设置迭代方向
    return iter;
}

listNode *listNext(listIter *iter);

listNode *listNext(listIter *iter)  //返回迭代器iter指向的当前节点并更新iter
{
    listNode *current = iter->next; //备份当前迭代器指向的节点

    if (current != NULL) {
        if (iter->direction == AL_START_HEAD)   //根据迭代方向更新迭代指针
            iter->next = current->next;
        else
            iter->next = current->prev;
    }
    return current;     //返回备份的当前节点地址
}

void listReleaseIterator(listIter *iter);

void listReleaseIterator(listIter *iter) {
    zfree(iter);
}

void listRewind(list *list, listIter *li);

void listRewind(list *list, listIter *li) {
    li->next = list->head;
    li->direction = AL_START_HEAD;
}

void listRewindTail(list *list, listIter *li);

void listRewindTail(list *list, listIter *li) {
    li->next = list->tail;
    li->direction = AL_START_TAIL;
}

list *listDup(list *orig); //拷贝表头为orig的链表并返回

内啥,看一下人家的注释吧,虽然是英文,但是不难看懂。

/* Duplicate the whole list. On out of memory NULL is returned.
 * On success a copy of the original list is returned.
 *
 * The 'Dup' method set with listSetDupMethod() function is used
 * to copy the node value. Otherwise the same pointer value of
 * the original node is used as value of the copied node.
 *
 * The original list both on success or error is never modified. */
list *listDup(list *orig)   //拷贝表头为orig的链表并返回
{
    list *copy;
    listIter iter;
    listNode *node;

    if ((copy = listCreate()) == NULL)  //创建一个表头
        return NULL;

    //设置新建表头的处理函数
    copy->dup = orig->dup;
    copy->free = orig->free;
    copy->match = orig->match;
    //迭代整个orig的链表
    listRewind(orig, &iter);    //为orig定义一个迭代器并设置迭代方向
    while((node = listNext(&iter)) != NULL) {    //迭代器根据迭代方向不停迭代
        void *value;

        //复制节点值到新节点
        if (copy->dup) {
            value = copy->dup(node->value); //如果定义了list结构中的dup指针,则使用该方法拷贝节点值。
            if (value == NULL) {
                listRelease(copy);
                return NULL;
            }
        } else
            value = node->value;    //获得当前node的value值

        if (listAddNodeTail(copy, value) == NULL) { //将node节点尾插到copy表头的链表中
            listRelease(copy);
            return NULL;
        }
    }
    return copy;    //返回拷贝副本
}

改版之后,把迭代器变成了变量,不再是指针。


listNode *listSearchKey(list *list, void *key);

listNode *listSearchKey(list *list, void *key)  //在list中查找value为key的节点并返回
{
    listIter iter;
    listNode *node;

    listRewind(list, &iter);    //创建迭代器
    while((node = listNext(&iter)) != NULL) {        //迭代整个链表
        if (list->match) {                          //如果设置list结构中的match方法,则用该方法比较
            if (list->match(node->value, key)) {
                return node;
            }
        } else {
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}

listNode *listIndex(list *list, long index);

listNode *listIndex(list *list, long index) {   //返回下标为index的节点地址
    listNode *n;

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

void listRotateTailToHead(list *list);

void listRotateTailToHead(list *list) {       //将尾节点插到头结点

    if (listLength(list) <= 1) return;  //只有一个节点或空链表直接返回

    listNode *tail = list->tail;
    
    /* Detach current tail */
    list->tail = tail->prev;        //取出尾节点,更新list的tail指针
    list->tail->next = NULL;
    /* Move it as head */
    list->head->prev = tail;        //将节点插到表头,更新list的head指针
    tail->prev = NULL;
    tail->next = list->head;
    list->head = tail;
}

void listRotateHeadToTail(list *list);

void listRotateHeadToTail(list *list) {
    if (listLength(list) <= 1) return;

    listNode *head = list->head;
    /* Detach current head */
    list->head = head->next;
    list->head->prev = NULL;
    /* Move it as tail */
    list->tail->next = head;
    head->next = NULL;
    head->prev = list->tail;
    list->tail = head;
}

void listJoin(list *l, list *o);

/* Add all the elements of the list 'o' at the end of the
 * list 'l'. The list 'other' remains empty but otherwise valid. */
void listJoin(list *l, list *o) {
    if (o->head)
        o->head->prev = l->tail;

    if (l->tail)
        l->tail->next = o->head;
    else
        l->head = o->head;

    if (o->tail) l->tail = o->tail;
    l->len += o->len;

    /* Setup other as an empty list. */
    o->head = o->tail = NULL;
    o->len = 0;
}

整体感觉

挺好,大家都可以写这个数据结构。

不过嘛,写的也不容易,咱分析的也不容易。

建议收藏,不然刷着刷着就可能找不到了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 拥抱STL - 类/结构体元素查询与排序

    我觉得,如果容器用来放基础数据类型,那真是浪费。 怎么说也要放个结构体或者类吧。

    看、未来
  • 依赖倒转原则 -- 强执若 or 弱执强?

    故事是别人的,不过放在这里也是很应景啦。 故事是这样的: 有个适龄小伙子,他还单着。有一天,他喜欢的那个姑娘突然给他打电话,说她的电脑坏了,一用就蓝屏警告。...

    看、未来
  • 学以致用C++设计模式 之 “备忘录模式”

    往事不堪回首,月如钩。寂寞梧桐深院锁清秋。春花秋月何时了,往事知多少?小楼昨夜又东风,故人不堪回首,月明中。

    看、未来
  • Python-列表+-01-两个列表各元素合并

    系统:Windows 7 语言版本:Anaconda3-4.3.0.1-Windows-x86_64 编辑器:pycharm-community-2016.3....

    zishendianxia
  • python 集合

    说明: 拿list_1每一个元素去list_2中查找,如果有,直接忽略,否则就直接输出。

    py3study
  • python 列表学习

    你可以对列表的数据项进行修改或者是更新,你也可以使用append()方法来添加列表项

    Mirror王宇阳
  • <算法入门>快速理解7种排序算法 | python3实现(附源码)学习难度:桶排序(简化版)冒泡排序选择排序插入排序快速排序(面试常用算法)归并排序(先分后和, 分而治之)希尔排序

    算法是程序的灵魂,而排序算法 是算法的入门经典,作者在此用python亲自实现了7种主流的排序算法,并做简短的说明. ? 排序算法 学习难度: 桶排序 < 冒泡...

    zhaoolee
  • Java中List集合去除重复数据的方法

    4.把list里的对象遍历一遍,用list.contain(),如果不存在就放入到另外一个list集合中

    三哥
  • R语言数据清洗实战——高效list解析方案

    list是R语言中包容性最强的数据对象,几乎可以容乃所有的其他数据类型。 但是包容性最强也也意味着他对于内部子对象的类型限制最少,甚至内部可以存在递归结构,这样...

    数据小磨坊
  • 经典排序算法和python详解(二):冒泡排序、双向冒泡排序、插入排序和希尔排序

    一、冒泡排序(Bubble Sort)二、冒泡排序法改进三、双向冒泡排序法四、插入排序五、希尔排序(插入排序改进)

    Minerva

扫码关注云+社区

领取腾讯云代金券