美文网首页
Redis 双端链表

Redis 双端链表

作者: wayyyy | 来源:发表于2022-09-13 00:27 被阅读0次

    链表在redis中使用非常广泛,比如列表键的底层实现之一就是链表。
    当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,redis就会使用链表最为列表键的底层实现。

    链表节点数据结构
    //  双端链表节点
    typedef struct listNode { 
        struct listNode* prev;  // 前置节点
        struct listNode* next;  // 后置节点
        void* value;            // 节点的值
    } listNode;
    
    image.png
    双端链表数据结构
    // 双端链表结构
    typedef struct list {
        listNode* head;
        listNode* tail;
        unsigned long len;
        void* (*dup)(void* ptr)     // 节点值复制函数
        void* (*free)(void* ptr)    // 节点值释放函数
        void* (*match)(void* ptr)   // 节点值对比函数
    }
    
    // 双端链表迭代器
    typedef struct listIter {
        listNode *next;  // 当前迭代到的节点
        int direction;   // 迭代的方向
    } listIter;
    
    image.png
    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;
    }
    
    image.png
    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;
    }
    
    image.png
    listAddNodeTail
    listInsertNode
    listDelNode
    迭代器
    /*
     * 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;
    }
    
    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;
    }
    
    listSearchKey
    listIndex
    listRotate
    void listRotate(list *list) {
        listNode *tail = list->tail;
    
        if (listLength(list) <= 1) return;
    
        /* Detach current tail */
        // 取出表尾节点
        list->tail = tail->prev;
        list->tail->next = NULL;
    
        /* Move it as head */
        // 插入到表头
        list->head->prev = tail;
        tail->prev = NULL;
        tail->next = list->head;
        list->head = tail;
    }
    

    特性总结

    Redis的链表实现的特性可总结如下:

    • 双端
      获取某个节点的前置节点和后置节点的复杂度都是O(1)。
    • 无环
      以NULL为终点
    • 带表头指针和表尾指针
      获取表头和表尾节点的复杂度为O(1)。
    • 带链表长度计数器
      获取链表长度的复杂度为O(1)。
    • 多态
      链表节点使用void*指针来保存节点值,并且可以通过list结构的三个属性节点值来设置类型特定函数,所以可用来保存不同类型的值。

    相关文章

      网友评论

          本文标题:Redis 双端链表

          本文链接:https://www.haomeiwen.com/subject/ilqvihtx.html