Redis源码学习简记(二) adlist原理与个人理解

redis中的adlist实质上是一个双向链表。里面实现了自己的iterator(包含正负方向)。作者对于链表写的真的是流畅,看得赏心悦目。没有一句多余的,很值得学习。

数据结构

总体来说分为三个数据类型。

list结构体,用于存储链表头结点,尾节点,使用者自定义的复制,释放与匹配函数(利用函数指针实现),还有长度。


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;

listNode结构体。每个节点的数据结构

typedef struct listNode {
    struct listNode *prev;//前指正
    struct listNode *next;//后指针
    void *value;//存储的数据
} listNode;

iterator结构体

typedef struct listIter {
    listNode *next;//下一个节点
    int direction;//方向 利用宏定义来判断
} listIter;
/* Directions for iterators */
#define AL_START_HEAD 0 //从前往后即为0
#define AL_START_TAIL 1//从后往前

垃圾字体画出来的图



在所提供的实现函数中,个人认为创建,插入,删除,复制,查找以及迭代器的操作比较重要。

listCreate

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

listEmpty清空链表元素,但是没有释放链表

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);//若有free函数则使用free清理value
        zfree(current);//删除节点
        current = next;//循环
    }
    list->head = list->tail = NULL;//链表初始化
    list->len = 0;
}

listRelease清空链表元素及自身,调用listEmpty

/* Free the whole list.
 *
 * This function can't fail. */
void listRelease(list *list)
{
    listEmpty(list);
    zfree(list);
}

listAddNodeHead头插法

list *listAddNodeHead(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)  //分配节点空间
        return NULL;
    node->value = value;//赋值
    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;//更新头结点
    }
    list->len++;//更新长度
    return list;
}

listAddNodeTail尾插法  与头插法基本一致

list *listAddNodeTail(list *list, void *value)
{
    listNode *node;

    if ((node = zmalloc(sizeof(*node))) == NULL)
        return NULL;
    node->value = value;
    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;
    }
    list->len++;
    return list;
}

listInsertNode 至指定的节点前插入或后面插入新节点返回插入后的链表

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

    if ((node = zmalloc(sizeof(*node))) == NULL)//分配新的空间
        return NULL;
    node->value = value;
    if (after) {//若after为真则表示新节点插在old_node后否则插在其前面
        node->prev = old_node;
        node->next = old_node->next;
        if (list->tail == old_node) {//判断为尾节点的情况
            list->tail = node;
        }
    } else {
        node->next = old_node;
        node->prev = old_node->prev;
        if (list->head == old_node) {//判断为头节点的情况
            list->head = node;
        }
    }
    if (node->prev != NULL) {//若不是头节点,要调整前面节点的next指针
        node->prev->next = node;
    }
    if (node->next != NULL) {//若不是尾节点,要调整后面节点的pre指针
        node->next->prev = node;
    }
    list->len++;//长度加一
    return list;
}

listDelNode 删除指定的节点

void listDelNode(list *list, listNode *node)
{
    if (node->prev)
        node->prev->next = node->next;//若存在前节点
    else
        list->head = node->next;//否则该节点为头结点
    if (node->next)
        node->next->prev = node->prev;//若存在后面的节点
    else
        list->tail = node->prev;//否则该节点为尾节点
    if (list->free) list->free(node->value);//若定义了free 释放value空间
    zfree(node);//释放节点
    list->len--;//长度减1
}

listGetIterator 获取迭代器

listIter *listGetIterator(list *list, int direction)
{
    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;
}

listNext 迭代器返回当前值,并且向后移

listNode *listNext(listIter *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;
}

listDup 复制一个相同的链表,返回为新的链表 (dup  dup2为linux中 文件描述符的复制的函数名)

list *listDup(list *orig)
{
    list *copy;
    listIter iter;
    listNode *node;

    if ((copy = listCreate()) == NULL) //
        return NULL;
    copy->dup = orig->dup; //拷贝相应的api函数
    copy->free = orig->free;
    copy->match = orig->match;
    listRewind(orig, &iter);//获取一个从后往前的迭代器。这里迭代器不是指针,省去了释放空间的操作。申请的空间在栈中。(这个小细节真是赞)
    while((node = listNext(&iter)) != NULL) {
        void *value;

        if (copy->dup) {//如存在复制函数则使用
            value = copy->dup(node->value);
            if (value == NULL) {
                listRelease(copy);
                return NULL;
            }
        } else
            value = node->value;//否则直接赋值
        if (listAddNodeTail(copy, value) == NULL) {//尾查法插入元素
            listRelease(copy);//失败则调用释放内存的函数释放,并返回NULL
            return NULL;
        }
    }
    return copy;
}

listSearchKey 根据可以key值找节点

listNode *listSearchKey(list *list, void *key)
{
    listIter iter; //同样使用的不是指针,省去释放烦恼。
    listNode *node;

    listRewind(list, &iter);//取得反向迭代器 与c++类似
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {//若有math函数则调用
            if (list->match(node->value, key)) {
                return node;
            }
        } else {
            if (key == node->value) {//否则直接判等
                return node;
            }
        }
    }
    return NULL;//若找不到则返回空
}
对于这一数据结构的学习,复习了对链表的基本操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值