Redis系列(六):数据结构List双向链表LPUSH、LPOP、RPUSH、RPOP、LLEN命令
1.介绍
redis中的list既实现了栈(先进后出)又实现了队列(先进先出)
1.示意图

2.各命令详解
LPUSH/RPUSH
LPUSH:
从队列的左边入队一个或多个元素
将所有指定的值插入到存于 key 的列表的头部。如果 key 不存在,那么在进行 push 操作前会创建一个空列表。 如果 key 对应的值不是一个 list 的话,那么会返回一个错误。
可以使用一个命令把多个元素 push 进入列表,只需在命令末尾加上多个指定的参数。元素是从最左端的到最右端的、一个接一个被插入到 list 的头部。
所以对于这个命令例子 LPUSH mylist a b c,返回的列表是 c 为第一个元素, b 为第二个元素, a 为第三个元素。
RPUSH:
从队列的右边入队一个元素
向存于 key 的列表的尾部插入所有指定的值。如果 key 不存在,那么会创建一个空的列表然后再进行 push 操作。 当 key 保存的不是一个列表,那么会返回一个错误。
可以使用一个命令把多个元素打入队列,只需要在命令后面指定多个参数。元素是从左到右一个接一个从列表尾部插入。 比如命令 RPUSH mylist a b c 会返回一个列表,其第一个元素是 a ,第二个元素是 b ,第三个元素是 c。
两个命令都返回list的长度
时间复杂度O(1) 相对于i++的操作
127.0.0.1:> lpush mylist c b a
(integer)
127.0.0.1:> rpush mylist d e f
(integer)
127.0.0.1:> lrange mylist
) "a"
) "b"
) "c"
) "d"
) "e"
) "f"
127.0.0.1:>
源码解析
{"rpush",rpushCommand,-,
"write use-memory fast @list",
,NULL,,,,,,},
{"lpush",lpushCommand,-,
"write use-memory fast @list",
,NULL,,,,,,},
LPUSH和RPUSH都是调的同一个函数通过传入LIST_HEAD和LIST_TAIL来判断怎么入队列
void lpushCommand(client *c) {
pushGenericCommand(c,LIST_HEAD);
}
void rpushCommand(client *c) {
pushGenericCommand(c,LIST_TAIL);
}
void pushGenericCommand(client *c, int where) {
int j, pushed = ;
robj *lobj = lookupKeyWrite(c->db,c->argv[]);
if (lobj && lobj->type != OBJ_LIST) {
addReply(c,shared.wrongtypeerr);
return;
}
for (j = ; j < c->argc; j++) {
if (!lobj) {
lobj = createQuicklistObject();
quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,
server.list_compress_depth);
dbAdd(c->db,c->argv[],lobj);
}
listTypePush(lobj,c->argv[j],where);
pushed++;
}
addReplyLongLong(c, (lobj ? listTypeLength(lobj) : ));
if (pushed) {
char *event = (where == LIST_HEAD) ? "lpush" : "rpush";
signalModifiedKey(c,c->db,c->argv[]);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[],c->db->id);
}
server.dirty += pushed;
}
LPOP/RPOP
LPOP:
移除并且返回 key 对应的 list 的第一个元素。
RPOP:
移除并返回存于 key 的 list 的最后一个元素。
两个命令都返回被移除的元素
时间复杂度O(1) 相对于i--的操作
127.0.0.1:> lpop mylist
"a"
127.0.0.1:> rpop mylist
"f"
127.0.0.1:> lrange mylist
) "b"
) "c"
) "d"
) "e"
127.0.0.1:>
源码解析
{"rpop",rpopCommand,,
"write fast @list",
,NULL,,,,,,},
{"lpop",lpopCommand,,
"write fast @list",
,NULL,,,,,,},
void lpopCommand(client *c) {
popGenericCommand(c,LIST_HEAD);
}
void rpopCommand(client *c) {
popGenericCommand(c,LIST_TAIL);
}
可见和push一样通过判断LIST_HEAD,来确定删除db中元素
void popGenericCommand(client *c, int where) {
robj *o = lookupKeyWriteOrReply(c,c->argv[],shared.null[c->resp]);
if (o == NULL || checkType(c,o,OBJ_LIST)) return;
robj *value = listTypePop(o,where);
if (value == NULL) {
addReplyNull(c);
} else {
char *event = (where == LIST_HEAD) ? "lpop" : "rpop";
addReplyBulk(c,value);
decrRefCount(value);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[],c->db->id);
if (listTypeLength(o) == ) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
c->argv[],c->db->id);
dbDelete(c->db,c->argv[]);
}
signalModifiedKey(c,c->db,c->argv[]);
server.dirty++;
}
}
LLEN
返回存储在 key 里的list的长度。 如果 key 不存在,那么就被看作是空list,并且返回长度为 0。 当存储在 key 里的值不是一个list的话,会返回error。
时间复杂度:O(1) 相当于常量操作
127.0.0.1:> llen mylist
(integer)
127.0.0.1:>
源码解析
{"llen",llenCommand,,
"read-only fast @list",
,NULL,,,,,,},
void llenCommand(client *c) {
robj *o = lookupKeyReadOrReply(c,c->argv[],shared.czero);
if (o == NULL || checkType(c,o,OBJ_LIST)) return;
addReplyLongLong(c,listTypeLength(o));
}
unsigned long listTypeLength(const robj *subject) {
if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
return quicklistCount(subject->ptr);
} else {
serverPanic("Unknown list encoding");
}
}
unsigned long quicklistCount(const quicklist *ql) { return ql->count; }
Redis系列(六):数据结构List双向链表LPUSH、LPOP、RPUSH、RPOP、LLEN命令的更多相关文章
- Redis(六):list/lpush/lrange/lpop 命令源码解析
上一篇讲了hash数据类型的相关实现方法,没有茅塞顿开也至少知道redis如何搞事情的了吧. 本篇咱们继续来看redis中的数据类型的实现: list 相关操作实现. 同样,我们以使用者的角度,开始理 ...
- Redis系列六:redis相关功能
一. 慢查询原因分析 与mysql一样:当执行时间超过阀值,会将发生时间耗时的命令记录 redis命令生命周期:发送 排队 执行 返回慢查询只统计第3个执行步骤的时间 预设阀值:两种方式,默认为10毫 ...
- Redis系列二 - 数据结构
前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...
- Redis系列(六):设置/移除键的过期时间
本篇博客是Redis系列的第6篇,主要讲解以下内容: 数据库数量 切换目标数据库 设置键的过期时间 移除键的过期时间 本系列的前5篇可以点击以下链接查看: Redis系列(一):Redis简介及环境安 ...
- redis 系列8 数据结构之整数集合
一.概述 整数集合(intset)是集合键的底层实现之一, 当一个集合只包含整数值元素,并且这个集合元素数量不多时, Redis就会使用整数集合作为集合键的底层实现.下面创建一个只包含5个元素的集合键 ...
- Redis系列(六)-SortedSets设计技巧
阅读目录: 介绍 Score占位 更多位信息 总结 介绍 Redis Sorted Sets是类似Redis Sets数据结构,不允许重复项的String集合.不同的是Sorted Sets中的每个成 ...
- redis 系列7 数据结构之跳跃表
一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...
- redis 系列6 数据结构之字典(下)
一.概述 接着上篇继续,这篇把数据结构之字典学习完, 这篇知识点包括:哈希算法,解决键冲突, rehash , 渐进式rehash,字典API. 1.1 哈希算法 当一个新的键值对 需要添加到字典里面 ...
- redis 系列5 数据结构之字典(上)
一. 概述 字典又称符号表(symbol table),关联数组(associative array), 映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中, ...
随机推荐
- Rocket - tilelink - ProbePicker
简单介绍ProbePicker的实现. 1. 基本介绍 用于把多个Cache client合并成一个: 2. diplomacy node ProbePicker的 ...
- Chisel3 - Tutorial - Parity
https://mp.weixin.qq.com/s/OtiQnE52PwdCpvmzJ6VFnA 奇偶发生器.统计输入中1的个数,如果为偶数则输出0,奇数则输出1. 参考链接: https: ...
- 多线程与RunLoop
iOS开发中的多线程 一个iOS程序,默认开启1条线程--主线程orUI线程 主线程主要作用: 显示/刷新UI界面 处理UI事件(点击.滚动.拖拽事件等) 主线程使用注 ...
- ASP.NET通过EntityFramework CodeFirst创建数据库
Number1 新建一个项目 给新项目添加一个实体数据模型 选择第三个 这里我创建两个有关系的类,也就是有外键关系的数据库表 using System; using System.Collection ...
- JavaScript实现html购物车代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Java实现约瑟夫斯问题
1 问题描述 引用自<算法设计与分析基础>第三版: 约瑟夫斯问题,是以弗拉瓦斯.约瑟夫斯(Flavius Josephus)的名字命名的.约瑟夫斯是一个著名的犹太历史学家,参加并记录了公元 ...
- 两条命令实现nodejs快速安装
操作系统: debian, ubuntu, fedora 当前版本: v14.4.0 一键安装命令: curl -sL https://deb.nodesource.com/setup_14.x | ...
- 小技巧:如何用 Chrome 将 SVG 转成 PNG
怎么把 SVG 转成 PNG ?用 Chrome 吧,非常好用,还能设定自己想要的尺寸. 什么是 SVG ? SVG (Scalable Vector Graphics,可缩放矢量图形) 是基于 XM ...
- DedeCms 标签中channelartlist设置属性标签样式的方法
实现的效果如下: {dede:channelartlist typeid='6' row='3' currentstyle='current'} <li class='{dede:field.c ...
- VMWare的三种网络连接方式
VMWare和主机的三种网络连接方式 桥接 这种模式下,虚拟机通过主机的网卡与主机通信,如果主机能够上网,则虚拟机也能联网. 在虚拟机中,需要将虚拟机的IP配置为与主机处于同一网段. 虚拟机也可以与同 ...