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

 

posted on 2017-09-26 22:46  qiezijiajia 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/dpains/p/7599441.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值