• Redis 设计与实现(第三章) -- 链表adlist


    概述


    1.链表介绍

    2.链表API

    链表介绍

    链表在Redis中的应用非常广泛,比如列表键list的底层实现就是使用的链表。除了列表键外,Redis的发布与订阅、慢查询、监视器等功能也用到了链表,Redis服务器本身还使用了链表来保存客户端连接状态,以后使用链表来构建客户端输出缓冲区。

    链表在Redis的数据结构如下:

    typedef struct listNode {
        struct listNode *prev;  //前一个节点
        struct listNode *next;  //后一个节点,可以看出链表为双向链表
        void *value;  //节点值
    } listNode;

    其实可以通过多个listNode链接起来,就是一个双向链表,但是使用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;

    Redis中链表的特点:

    1.双向,有prev和next指针指向前/后一个节点;

    2.无环,header的prev和tail的next都指向null;

    3.带表头和表尾指针,快速获取表头/尾;

    4.带链表长度值;

    5.多态,通过void* 来保存节点值,可以通过函数为节点值设置类型特定函数,所以链表能够保存各种不同类型的值。

    链表API

    添加头部节点,和基本链表操作类似

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

    search节点

    listNode *listSearchKey(list *list, void *key)
    {
        listIter *iter;
        listNode *node;
    
        iter = listGetIterator(list, AL_START_HEAD); //获取遍历器
        while((node = listNext(iter)) != NULL) {  //遍历节点
            if (list->match) { //match函数不为空,即设置了链表的值比较函数
                if (list->match(node->value, key)) {  //根据match函数来比较
                    listReleaseIterator(iter);
                    return node;
                }
            } else {  //没设置,直接比较
                if (key == node->value) {
                    listReleaseIterator(iter);
                    return node;
                }
            }
        }
        listReleaseIterator(iter);
        return NULL;
    }

    遍历器

    typedef struct listIter {  //设置一个listIter遍历器
        listNode *next;
        int direction; //direction值为AL_START_HEAD(0)头部节点开始,AL_START_TAIL(1)尾部节点开始
    } listIter;

    获取迭代器的方法:

    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; //设置next节点为head
        else
            iter->next = list->tail;
        iter->direction = direction;
        return iter;
    }

    然后通过next函数通过迭代器遍历:

    /* Return the next element of an iterator.
     * It's valid to remove the currently returned element using
     * listDelNode(), but not to remove other elements.
     *
     * The function returns a pointer to the next element of the list,
     * or NULL if there are no more elements, so the classical usage patter
     * is:
     *使用方法
     * iter = listGetIterator(list,<direction>);
     * while ((node = listNext(iter)) != NULL) {
     *     doSomethingWith(listNodeValue(node));
     * }
     *
     * */
    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;
    }
  • 相关阅读:
    postman是如何使用的?
    PHPstudy如何在本地搭建多站点
    2017 电影推荐
    MySQL 约束、表连接、表关联、索引
    MySQL 基本语句(1)
    MySQL 数据类型
    Python3 基础
    【LOJ】 #2665. 「NOI2013」树的计数
    【LOJ】#2244. 「NOI2014」起床困难综合症
    【51nod】1251 Fox序列的数量
  • 原文地址:https://www.cnblogs.com/dpains/p/7599441.html
Copyright © 2020-2023  润新知