目录

redis源码分析系列文章

前言

API使用

lpush左侧插入数据

rpush右侧插入数据

删除某个数据

修改某个数据

具体逻辑图

双向链表的定义

节点ListNode

整体架构

双向链表的实现

创建表头

清空表

添加元素到表头

添加元素到表尾

插入

删除

总结


redis源码分析系列文章

[Redis源码系列]在Liunx安装和常见API

为什么要从Redis源码分析

String底层实现——动态字符串SDS

前言

hello,又见面了。不要问为什么,问就是勤劳。马上要开启爆更模式啦。在Redis中链表List的应用非常广泛,但是Redis是采用C语言来写,底层采用双向链表实现(这边提一嘴,如果是科班出身或者大学有学过数据结构的同学,可以划走啦)。我们今天的重点就是双向链表。

API使用

先来使用一下API。如果之前有用过的同学,可以直接跳到下一小节。

lpush左侧插入数据

使用lpush命令往list的左侧中插入a,b,c三个字符,这边注意顺序,查询出来的是c,b,a。下面会说为什么,先挖个坑。

rpush右侧插入数据

使用rpush命令往list中插入d,e两个字符,查询出来的顺序是和我们想的一样,最后两位是d,e。

删除某个数据

使用lrem命令删除a字符,那么中间1代表什么意思呢?其为count,表示移除列表中与a相等的元素个数。即如果count>0,表示从表头开始向表尾搜索,移除count个与a相等的元素。如果count<0,表示从表尾开始向表头搜索,移除count个与a相等的元素。如果count=0,移除所有与a相等的元素,因为是移除所有,所以不管从表头还是表尾,结果是一样的。

修改某个数据

使用lset命令将mylist的下标为1的元素修改为dd,原来list为c ,b,d,e,修改后的结果为c,dd,d,e。

具体逻辑图

这边看不懂没关系,下面会针对每个模块详细说明。

双向链表的定义

节点ListNode

包括头指针prev,尾指针next,当前的值value,如下图所示。每个节点都有两个指针,既能从表头根据尾指针找到表尾,又能从表尾根据头指针prev找到表头,如果将他们连起来,就构成了双向链表。

具体代码如下:

//定义链表节点的结构体
typedef struct listNode {
//前面一个节点的指针
struct listNode *prev;
//后面一个节点的指针
struct listNode *next;
//当前节点的值的指针 ,因为值的类型不确定
void *value;
} listNode;

整体架构

包括头指针head,尾指针tail,整个链表长度len,一些函数(个人认为不重要,如果有知道的小伙伴欢迎评论),如下图所示。头指针head指向整个链表的第一个节点,尾指针tail指向整个链表的最后一个节点。

具体代码如下:

//定义链表,对链表节点的再封装
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;

双向链表的实现

创建表头

我们创建list表结构,首先需要判断当前是否有可分配的空间来创建,使用zmalloc方法来分配空间,如果分配不了,则返回NULL,如果可以分配,则继续。接着赋值list的头节点head和尾节点tail为NULL,len为0,赋值相关函数为NULL。最后返回结果list。

//创建一个表头,返回值是链表结构的指针
list *listCreate(void)
{
struct list *list;
//尝试分配空间
if ((list = zmalloc(sizeof(*list))) == NULL)
return NULL;
//相关属性赋值
list->head = list->tail = NULL;
list->len = ;
list->dup = NULL;
list->free = NULL;
list->match = NULL;
//最终结果返回
return list;
}

清空表

传入list的指针,首先定义当前节点current,使其指向头指针,定义len,使其等于list的长度。接着进行循环,每次len减一,定义新节点next,始终指向当前节点current的下一个节点,如果有值,则释放该节点,当前节点current后移,next节点同样后移。直到len为0,释放完所有节点,退出循环。最后赋值list的头节点head和尾节点tail为NULL,len为0。

注意:这边和SDS一样,清空并不是直接删除list,而是删除其数据,外层的list结构仍然存在。这其实上是惰性删除。

void listEmpty(list *list)
{
unsigned long len;
//定义两个节点指针current和next
listNode *current, *next;
//当前节点指针current指向list的头节点位置,即list的第一个数据
current = list->head;
//len为list的长度
len = list->len;
//开始循环,每次len减1
while(len--) {
//先让下一个指针指向下一个节点,因为底下直接释放当前节点,如果不在此处复制,底下就获取不到了
next = current->next;
//释放当前节点的值
if (list->free) list->free(current->value);
//释放当前节点
zfree(current);
//当前节点等于刚才的下一个节点next,即开始往后移,开始下一轮循环
current = next;
}
//释放完给头指针head,尾指针tail赋值为NULL
list->head = list->tail = NULL;
//len赋值0
list->len = ;
}

添加元素到表头

添加元素到表头,首先新建一个新节点node,判断是否有内存分配,如果有,则继续,如果没有,则返回NULL,退出方法。这边新节点是用来存在输入参数中的value的,所以需要内存。接着将新节点node的value值赋值为输入参数value。最后需要调整list的头指针,尾指针,原来第一个节点的指针情况(这边看下图,描述起来有点混乱,图片一目了然)。最最后,就是list的len加1,返回list。

举个例子,如果要在list中插入节点f,首先将节点的头指针赋值为空(对应步骤1),然后将新节点的尾指针next指向第一个节点(对应步骤2),将第一个节点的prev指向新节点(对应步骤3),最后将list的头指针head指向新节点(对应步骤4)。这边需要注意的是,步骤2和步骤3需要在步骤4前面,不然会找到第一个节点。

具体代码如下:

//添加一个元素到表头
list *listAddNodeHead(list *list, void *value)
{
listNode *node; if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
node->value = value;//为当前节点赋值
//如果当前list为空
if (list->len == ) {
list->head = list->tail = node;//头尾指针都指向改节点
node->prev = node->next = NULL;//当前节点的头尾指针都为null
} else {//如果当前list不为空
node->prev = NULL;//新节点的头指针为null
node->next = list->head;//新节点的尾指针指向原来的尾指针
list->head->prev = node;//原来的第一个节点的头指针指向新节点
list->head = node;//链表的头指针指向新节点
}
list->len++;//list长度+1
return list;
}

添加元素到表尾

添加元素到表尾,首先新建一个新节点node,判断是否有内存分配,如果有,则继续,如果没有,则返回NULL,退出方法。这边新节点是用来存在输入参数中的value的,所以需要内存。接着将新节点node的value值赋值为输入参数value。最后需要调整list的头指针,尾指针,原来最后一个节点的指针情况(这边看下图,描述起来有点混乱,图片一目了然)。最最后,就是list的len加1,返回list。

举个例子,如果要在list中插入节点f,首先将节点的尾指针赋值为空(对应步骤1),然后将新节点的头指针指向最后一个节点(对应步骤2),将最后一个节点的next指向新节点(对应步骤3),最后将list的尾指针tail指向新节点(对应步骤4)。

步骤如下:

//添加元素到表尾
list *listAddNodeTail(list *list, void *value)
{
//新建节点node
listNode *node;
//尝试分配内存
if ((node = zmalloc(sizeof(*node))) == NULL)
return NULL;
//为新节点node赋值
node->value = value;
//调整指针
if (list->len == ) {
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;
}
//len加1
list->len++;
return list;
}

插入

为list的某个节点old_node的after(前后)查询新值value,首先新建一个新节点node,判断是否有内存分配,如果有,则继续,如果没有,则返回NULL,退出方法。这边新节点是用来存在输入参数中的value的,所以需要内存。(这段话是不是听的耳朵都起茧子啦

双向链表都不懂,还说懂Redis?的更多相关文章

  1. 你真的懂Redis的5种基本数据结构吗?

    摘要: 你真的懂Redis的5种基本数据结构吗?这些知识点或许你还需要看看. 本文分享自华为云社区<你真的懂Redis的5种基本数据结构吗?这些知识点或许你还需要看看>,作者:李子捌. 一 ...

  2. 你真的懂redis吗?

    Redis在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在Redis的使用和原理方面对小伙伴们进行各种刁难.作为一名在互联网技术行业打击过成百上千名[请允许我夸张一下]的资深技术面试官 ...

  3. 看懂redis配置文件

    看懂redis 配置文件: https://blog.csdn.net/liqingtx/article/details/60330555 redis 数据库缓存双写一致性解决方案: https:// ...

  4. 这么简单的 Redis 面试题都不懂,怎么拿offer?

    来源:mp.weixin.qq.com/s/daBkliC8dAT_zYyoLiS7WA 随着系统访问量的提高,复杂度的提升,响应性能成为一个重点的关注点.而缓存的使用成为一个重点.redis 作为缓 ...

  5. Redis高级项目实战,都0202年了,还不会Redis?

    导读 大家都听过1万小时定律,可事实真的是这样吗?做了1万小时的CRUD,不还只会CRUD吗,这年头不适当的更新自身下技术栈,出门和别人聊天吹牛的时候,都没拿不出手,(⊙o⊙)…Redis没入门的童鞋 ...

  6. Phaser都不懂,还学什么多线程

    前面的文章中我们讲到了CyclicBarrier.CountDownLatch的使用,这里再回顾一下CountDownLatch主要用在一个线程等待多个线程执行完毕的情况,而CyclicBarrier ...

  7. 一文读懂Redis持久化

    Redis 是一个开源( BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件.它支持的数据类型很丰富,如字符串.链表.集合.以及散列等,并且还支持多种排序功能. 什么叫持久 ...

  8. 一文读懂Redis的四种模式,单机、主从、哨兵、集群

    少点代码,多点头发 本文已经被GitHub收录,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 入职第一周,我被坑了 最近刚入职 ...

  9. 一文读懂Redis常见对象类型的底层数据结构

    Redis是一个基于内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.Redis支持五种常见对象类型:字符串(String).哈希(Hash).列表(List).集合(Set)以及有序集合( ...

随机推荐

  1. PAT 1011 World Cup Betting (20分) 比较大小难度级别

    题目 With the 2010 FIFA World Cup running, football fans the world over were becoming increasingly exc ...

  2. Scrapy 框架 入门教程

    Scrapy入门教程 在本篇教程中,我已经安装好Scrapy 本篇教程中将带您完成下列任务: 创建一个Scrapy项目 定义提取的Item 编写爬取网站的 spider 并提取 Item 编写 Ite ...

  3. SourceTree 3.1.3 版本跳过bitbucket注册方法(亲测好用)

    1.首先下载并安装好git程序. 2.接着下载并执行SourceTreeSetup-3.1.3.exe,会进入登录或注册bitbucket的界面,我只是想用软件,并不想去注册账号.怎么办?请往下看. ...

  4. matlab第六章数据分析与多项式计算

    MATLAB练习 第六章数据分析与多项式计算 1.max和min 1.分别求矩阵A中各列和各行元素中的最大值.max和min的用法一样 % [例6.1]分别求矩阵中各列和各行元素中的最大值. A=[5 ...

  5. [转载] Objectiv-C 入门一二三

    http://my.oschina.net/fuckphp/blog/92993 http://my.oschina.net/fuckphp/blog/93217 http://my.oschina. ...

  6. PAT-1059 Prime Factors (素数因子)

    1059. Prime Factors Given any positive integer N, you are supposed to find all of its prime factors, ...

  7. 在DAO的查询操作里,数据库查询到记录,sql语句也成功执行,但是返回的对象是null

     在这里  如果改成User user=null; 后面 对user对象的赋值是会失败的. 原因: 要赋值的话,一定要有对象,要new一下给对象分配空间然后再给对象赋值.

  8. Python_OpenCV学习记录01安装

    Python照样快! 众所周知,虽然Python语法简洁,编写高效,但相比C/C++运行慢很多.然 而Python还有个重要的特性:它是一门胶水语言!Python可以很容易地扩展 C/C++.Open ...

  9. jpype2

    # -*- coding: utf-8 -*-# @Time : 2020/5/21 0:04# 从环境变量获取jvm虚拟机安装路径,若为None则获取默认路径import os import jpy ...

  10. 添加nginx新模块,获取客户真实ip

    当前是客户端登录软件后台获取不到客户的真实ip而是云盾的代理ip 为了获取到真实ip后来发现通过配置nginx的read_ip模块就可以了 获取客户的真实ip使用Nginx的realip模块 当Ngi ...