Redis链表(adlist)--含源码

链表作为经典且常用的数据结构,在很多高级编程语言中都内置了链表这种数据额结构,但是Redis是有C语言实现,并没有内置这种数据结构,所以Redis自身构建了链表。

节点和链表的实现

listNode结构表示为:

typedef struct listNode {
    struct listNode *prev;	// 指向前一个元素的指针
    struct listNode *next;	// 指向后一个元素的指针
    void *value;	// 可指向任意类型
} 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;
  • dup函数用于复制链表节点保存的值。当链表需要复制、移植等操作的时候,便会用到dup函数来复制节点的值。因为元素的值可能是一个自定义的结构体或者其他类型的数据结构,存在着指针等成员,如果直接用=号复制会造成隐患(可以重构=来改善)。如果不想使用dup函数,可以将dup赋值为NULL,这样就可以直接使用默认操作符=来复制节点内容。
  • free函数用于释放链表节点保存的值。也是为了一些指针成员变量而实现的,一般指针成员都需要手动分配一块内存来使用,若是不相应的调用free来释放内存,将会导致内存泄露。同dup一样,free在赋值NULL之后,便会调用Redis自带的zfree函数来释放。
  • match函数用于比较链表节点保存的值与另一值是否相等。在通过一个值在链表查找是否拥有该元素时,如果match函数没有被赋值为NULL,则通过match函数来比较两个值是否相等。

下图展示由3个listNode组成的list
在这里插入图片描述
Redis链表特性:

  • 双向:可以在时间复杂度为O(1)的情况下获取某个节点的前置节点和后置节点
    -len属性:可在时间复杂度为O(1)的情况下获取链表长度
  • headtail指针:方便访问头尾节点
  • 多态:void *保存节点的值,并且dupfreematch可为节点值设置类型特定的函数,是链表可以保存多种类型
关于链表的一些源码

Redis链表这部分算是比较简单的,源码中一些API的实现的代码也容易看懂。以下对一些源码做一些简单的说明,不感兴趣的可以不看。
listIter:

typedef struct listIter {
    listNode *next;	// 指向下一个元素的指针
    int direction;	// 遍历方向
} listIter;

#define AL_START_HEAD 0
#define AL_START_TAIL 1

实现一个链表的迭代器,是为了方便的遍历整个链表。direction的值取为AL_START_HEAD或者AL_START_TAIL,分别代表从头部开始遍历,及从尾部遍历。比如说当directionAL_START_HEAD时,next跟listNode中的next值相同,当directionAL_START_TAIL时,nextlistNode中的prev值相同。

listCreate

list *listCreate(void)
{
    struct list *list;	// 链表的结构体

    if ((list = zmalloc(sizeof(*list))) == NULL)	// 分配空间
        return NULL;	// 出错是返回NULL,否则返回链表指针
    list->head = list->tail = NULL;
    list->len = 0;
    list->dup = NULL;
    list->free = NULL;
    list->match = NULL;
    return list;
}

创建并初始化一个链表。可用AlFreeList()函数释放创建的链表。但是调用AlFreeList()之前需要释放每个节点。

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函数,如果有,直接使用
        zfree(current);	// 没有定义free,则使用内部封装好的zfree释放节点
        current = next;	// 指向下一个节点
    }
    list->head = list->tail = NULL;
    list->len = 0;
}

清空整个链表但不破坏链表本省,从head开始,通过链表长度len迭代释放节点。

listRelease

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) {	// 链表长度为0的情景
        list->head = list->tail = node;	// 头尾指针都指向该节点
        node->prev = node->next = NULL;	// 节点的前后指针指向NULL
    } else {	// 链表长度非0的情景
        node->prev = NULL;
        node->next = list->head;
        list->head->prev = node;
        list->head = node;
    }
    list->len++;	// 增加链表长度
    return list;
}

为一个元素申请一个节点,并添加到链表最前端。出错时,返回NULL并且不执行任何操作(即链表保持不变)。成功时,返回传递给函数的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;
}

方法和listAddNodeHead一样,listAddNodeTail将节点添加到最末端。

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) {	// 插入一个新节点在指定节点后
        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) {
        node->prev->next = node;
    }
    if (node->next != NULL) {
        node->next->prev = node;
    }
    list->len++;
    return list;
}

为一个元素创建节点,并插入在指定节点前或者指定节点后。after参数来控制节点插入的位置,after为0则插入在指定节点前,after为非0则插入在指定节点后。

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);
    zfree(node);	//释放节点
    list->len--;	//链表长度自减
}

从链表当中删除指定的一个节点。

listGetIteratorlistReleaseIteratorlistRewindlistRewindTaillistNext

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

/* Release the iterator memory */
void listReleaseIterator(listIter *iter) {
    zfree(iter);
}

/* Create an iterator in the list private iterator structure */
void listRewind(list *list, listIter *li) {
    li->next = list->head;
    li->direction = AL_START_HEAD;
}

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

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

五个函数全部跟迭代器相关,分别为创建迭代器、销毁迭代器、重置迭代器到链表头部、重置迭代器到链表尾部、获取迭代器下一个节点。

listNode:返回迭代器的下一个元素。 使用listDelNode()删除当前返回的元素是有效的,但不删除其他元素。该函数返回指向列表下一个元素的指针,如果没有更多元素,则返回NULL

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;
    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);
            return NULL;
        }
    }
    return copy;
}

复制整个列表。 在内存不足时返回NULL。成功时,将返回原始列表的副本。

listSearchKey

listNode *listSearchKey(list *list, void *key)
{
    listIter iter;
    listNode *node;

    listRewind(list, &iter);	// 创建迭代器,迭代访问源链表
    while((node = listNext(&iter)) != NULL) {
        if (list->match) {	// 如果定义了match函数则只用
            if (list->match(node->value, key)) {
                return node;
            }
        } else {	// 没有定义match,则使用=
            if (key == node->value) {
                return node;
            }
        }
    }
    return NULL;
}

在列表中搜索与给定键匹配的节点。成功时,返回第一个匹配的节点指针(从头开始搜索)。 如果不存在匹配节点,则返回NULL。

listIndex

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

返回指定的从零开始的索引处的元素,其中0是头部,1是头部旁边的元素,依此类推。 使用负整数以便从尾部计数,-1是最后一个元素,-2是倒数第二个,依此类推。 如果索引超出范围,则返回NULL。

listRotate

void listRotate(list *list) {
    listNode *tail = list->tail;

    if (listLength(list) <= 1) return;	// 链表长度小于或等于1,直接返回

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

旋转列表,删除尾节点并将其插入头部

listJoin

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

在列表’l’的末尾添加列表’o’的所有元素。 列表’o’置为空,但有效。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值