redis源码分析-adlist(链表)

一、简介

上一节,介绍了sds结构,接下来将介绍下redis的adlist(又称链表).redis的链表采用双向链表方式进行实现,代码位于src/adlist.h、src/adlist.c .

二、数据结构

C语言数据结构中双向链表结构由节点(listNode)、迭代器(listIterator)、容器(list)组成,redis也是如此。
  1. 节点

    typedef struct listNode {
        struct listNode *prev;
        struct listNode *next;
        void *value;
    } listNode;

    value:节点的内容 prev和next分别代表指向上一个节点与下一个节点的指针。

  2. 迭代器

    typedef struct listIter {
         listNode *next;
         int direction;
    } listIter;

    next:下一个节点指针 direction:遍历方向

  3. 容器

    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;

    head:表头指针 tail表尾指针 dup:复制函数 free:释放函数 match:遍历查找函数 len:链表长度

三、链表操作方法

上面简单地介绍了下链表的结构,下面将介绍链表的相关函数。链表的操作函数,相信大家在C语言数据结构都有接触过,实现代码比较简单。
  1. listCreate: 创建一个list,并初始化,然后返回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;
    }
  2. listRelease: 与listCreate对应,该函数实现链表释放功能。

    void listRelease(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);
            zfree(current);
            current = next;
        }
        zfree(list);
    }

    细心的读者可能注意到 list->free函数,这个函数是用于释放节点内容的空间。

  3. 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;
    }
  4. listAddNodeTail: 与listAddNodeHead功能类似,添加数据至表尾。

    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;
    }
  5. listInsertNode: 将新节点添加至指定的节点之前/之后

    //若after=0 ,将新节点插入到 old_node 之前。
    //若after=1 ,将新节点插入到 old_node 之后。
    list *listInsertNode(list *list, listNode *old_node, void *value, int after) {
        listNode *node;
    
        if ((node = zmalloc(sizeof(*node))) == NULL)
            return NULL;
        node->value = value;
        //修改node前后指针指向
        if (after) {
            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;
            }
        }
        //如果新插入元素的前节点非空,则修改前节点的next指针指向
        if (node->prev != NULL) {
            node->prev->next = node;
        }
        //如果新插入元素的后节点非空,则修改后节点的prev指针指向
        if (node->next != NULL) {
            node->next->prev = node;
        }
        list->len++;
        return list;
    }
  6. listDelNode: 删除指定节点操作

    void listDelNode(list *list, listNode *node)
    {
    
        //修改node前节点的next指向
        if (node->prev)
            node->prev->next = node->next;
        else
            list->head = node->next;
        //修改node前节点的prev指向
        if (node->next)
            node->next->prev = node->prev;
        else
            list->tail = node->prev;
        //释放node节点
        if (list->free) list->free(node->value);
        zfree(node);
        //len-1
        list->len--;
    }
  7. listGetIterator: 获取迭代器

    //direction:有两个值
    //AL_START_HEAD : 从表头向表尾进行迭代
    //AL_START_TAIL : 从表尾到表头进行迭代
    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;
    }
  8. listRewind: 重置链表指针到表头。与之对应,listRewindTail的功能为重置指针至表尾。

    
    void listRewind(list *list, listIter *li) 
    {
        li->next = list->head;
        li->direction = AL_START_HEAD;
    }
  9. listNext: 该函数非常重要,在很多链表操作都有使用,实现了链表遍历的功能。

    
    listNode *listNext(listIter *iter)
    {
        //获取下一个指针
        listNode *current = iter->next;
        //如果current非空,则根据direction值,修改iter->next指向
        if (current != NULL) {
            if (iter->direction == AL_START_HEAD)
                iter->next = current->next;
            else
                iter->next = current->prev;
        }
        return current;
    }
  10. listDup: 复制链表功能

    
    list *listDup(list *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);
                if (value == NULL) {
                    listRelease(copy);
                    return NULL;
                }
            } else
                value = node->value;
            //添加数据至表尾
            if (listAddNodeTail(copy, value) == NULL) {
                listRelease(copy);
                return NULL;
            }
        }
        return copy;
    }
  11. listSearchKey:根据给出的key对链表进行查找

    
    listNode *listSearchKey(list *list, void *key)
    {
        listIter iter;
        listNode *node;
        //重置指针
        listRewind(list, &iter);
        //遍历
        while((node = listNext(&iter)) != NULL) {
            //对比value
            if (list->match) {
                if (list->match(node->value, key)) {
                    return node;
                }
            } else {
                if (key == node->value) {
                    return node;
                }
            }
        }
        return NULL;
    }
  12. listIndex: 根据给出的下标,查找相应的节点。

    //根据给出的下标进行搜索
    //index>=0: 顺序遍历,从表头往表尾进行遍历
    //index<0: 逆向遍历,从表尾往表头进行遍历
    listNode *listIndex(list *list, long 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;
    }
    

四、总结

1. redis的链表采用了双向非循环链表设计。
2. redis对链表并没有做太大的改造,与C语言数据结构链表功能大同小异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值