本系列文章阅读的源码都是Redis-3.0.3版本,编译时使用的是libc库。
链表是常用常见的数据结构,是数据结构课程中比较基础,相对好理解的一种。c语言中没有内置链表数据结构,Redis自己构建了链表adlist。在adlist学习一中,学习了创建,头插入和尾插入结点函数,并写了代码验证相关函数,以及迭代器打印结点值。本节会看剩下的adlist的相关函数
listInsertNode该函数从字面理解是,在list中插入一个新结点,那肯定得知道是在哪个位置插入新结点,得提供一个相对位置,又分为在已知结点之前或之后插入新结点。
list*listInsertNode(list*list,listNode*old_node,void*value,intafter){listNode*node;if((node=zmalloc(sizeof(*node)))==NULL)//创建要新插入的节点returnNULL;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++;//更新链表节点数returnlist;}
函数参数中,old_node是要插入新结点的相关位置,又用参数after来控制新插入的结点,如果after非0,则在old_node之后插入新结点,如果为0,则在old_node之前插入新结点。要插入的位置找到了,后续主要就是指针的调整,特别注意当old_node为头结点或者尾结点的情况,最后是链表结点数目的更改。
listDelNodevoidlistDelNode(list*list,listNode*node){if(node-prev)//调整前置节点的指针node-prev-next=node-next;elselist-head=node-next;if(node-next)//调整后置节点的指针node-next-prev=node-prev;elselist-tail=node-prev;if(list-free)list-free(node-value);//释放值zfree(node);//释放节点list-len--;//链表数减一}
有上面的插入新结点,那就有删除结点的操作。必须要告知要删除的结点,知道结点就可以调整prev,next的指针指向,并在调整指针之后,释放要删除结点的内存,链表长度减一。
listSearchKey像数组一样,可以在链表中查找给定的值是否存在,数组是连续区域并比较下标对应的值,而链表中比较值是通过调整结点的指针,一个结点一个结点中进行比较对应的结点值。
listNode*listSearchKey(list*list,void*key){listIter*iter;listNode*node;iter=listGetIterator(list,AL_START_HEAD);//迭代整个链表while((node=listNext(iter))!=NULL){if(list-match){//对比if(list-match(node-value,key)){listReleaseIterator(iter);returnnode;//找到}}else{if(key==node-value){listReleaseIterator(iter);returnnode;//找到}}}listReleaseIterator(iter);returnNULL;}
链表查找,默认是从链表头查询到链表尾,其实也可以从链表尾进行查找,迭代器从尾部开始扫描进行比对链表结点中value所代表的值。
注意代码中有判断list-match指针是否为空,如果不为空,证明该函数指针指向具体的结点对比的函数,就直接调用该函数指针指向的函数进行链表结点的比对,看是否符合key所代表的的内容。后面再进行结点存储字符串时会进行相应的代码验证。
listIndexlistNode*listIndex(list*list,longindex){listNode*n;if(index0){//如果索引为负数,从表尾开始查找index=(-index)-1;n=list-tail;while(index--n)n=n-prev;}else{//如果索引为正数,从表头开始查找n=list-head;while(index--n)n=n-next;}returnn;}
该函数返回链表在给定索引上的值索引以0为起始,也可以是负数,-1表示链表最后一个节点,依此类推,如果索引超出范围(outofrange),返回NULL。
主要的adlist函数就介绍到这儿,后续会进行adlist的结点存储字符串,及match函数指针指向具体的函数来进行查找结点值。
如果有错误及不当之处,请批评指正。如果对您有一点帮助,我就十分高兴,希望共同进步,也会继续学习并分享,请