链表在redis中使用非常频繁,常用的list数据就是使用链表数据结构,除此之外,还有发布与订阅、监视器也用到了链表。
数据结构
redis的链表由多个链表节点组成,每个链表节点分别保存了前驱和后继节点,形成了一个双向链表。最终的链表会包含一个头节点、尾节点以及链表长度。具体定义如下。
1、链表节点
typedef struct listNode { // 前驱节点 struct listNode *prev; // 后继节点 struct listNode *next; // 节点数据 void *value; } listNode;
2、链表
typedef struct list { // 链表头 listNode *head; // 链表尾 listNode *tail; // 链表包含的节点数量 unsigned long len; // 节点复制函数 void *(*dup)(void *ptr); // 节点释放函数 void (*free)(void *ptr);】 // 节点比较函数 int (*match)(void *ptr, void *key); } list;
特点
1、双端。每个节点都有前驱和后继节点。
2、无环。链表头节点的前驱和链表的尾节点的后继都是null。
3、自带链表长度。查找链表长度为O(1)。
4、多态。用户可以自己定义复制、释放以及比较函数。
源码
1、链表初始化
/* Create a new list. The created list can be freed with * listRelease(), but private value of every node need to be freed * by the user before to call listRelease(), or by setting a free method using * listSetFreeMethod. * * On error, NULL is returned. Otherwise the pointer to the new 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、增加节点
节点的增加操作可以在头和尾都能进行。
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; }