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命令的更多相关文章

  1. Redis(六):list/lpush/lrange/lpop 命令源码解析

    上一篇讲了hash数据类型的相关实现方法,没有茅塞顿开也至少知道redis如何搞事情的了吧. 本篇咱们继续来看redis中的数据类型的实现: list 相关操作实现. 同样,我们以使用者的角度,开始理 ...

  2. Redis系列六:redis相关功能

    一. 慢查询原因分析 与mysql一样:当执行时间超过阀值,会将发生时间耗时的命令记录 redis命令生命周期:发送 排队 执行 返回慢查询只统计第3个执行步骤的时间 预设阀值:两种方式,默认为10毫 ...

  3. Redis系列二 - 数据结构

    前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...

  4. Redis系列(六):设置/移除键的过期时间

    本篇博客是Redis系列的第6篇,主要讲解以下内容: 数据库数量 切换目标数据库 设置键的过期时间 移除键的过期时间 本系列的前5篇可以点击以下链接查看: Redis系列(一):Redis简介及环境安 ...

  5. redis 系列8 数据结构之整数集合

    一.概述 整数集合(intset)是集合键的底层实现之一, 当一个集合只包含整数值元素,并且这个集合元素数量不多时, Redis就会使用整数集合作为集合键的底层实现.下面创建一个只包含5个元素的集合键 ...

  6. Redis系列(六)-SortedSets设计技巧

    阅读目录: 介绍 Score占位 更多位信息 总结 介绍 Redis Sorted Sets是类似Redis Sets数据结构,不允许重复项的String集合.不同的是Sorted Sets中的每个成 ...

  7. redis 系列7 数据结构之跳跃表

    一.概述 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的.在大部分情况下,跳跃表的效率可以和平衡树(关系型数据库的索引就是平衡树 ...

  8. redis 系列6 数据结构之字典(下)

    一.概述 接着上篇继续,这篇把数据结构之字典学习完, 这篇知识点包括:哈希算法,解决键冲突, rehash , 渐进式rehash,字典API. 1.1 哈希算法 当一个新的键值对 需要添加到字典里面 ...

  9. redis 系列5 数据结构之字典(上)

    一. 概述 字典又称符号表(symbol table),关联数组(associative array), 映射(map),是一种用于保存键值对(key-value pair)的抽象数据结构.在字典中, ...

随机推荐

  1. Postman学习笔记(一)

    一.简介 Postman是一种网页调试与发送网页 http 请求的 chrome 插件.我们可以用来很方便的 模拟 get 或者 post 或者其他方式的请求来调试接口. 二.安装 1.chrome浏 ...

  2. Rocket - tilelink - RegionReplicator

    https://mp.weixin.qq.com/s/XZVCdt50tM6lavchGm9GRg   简单介绍RegionReplicator的实现.   ​​   1. 基本介绍   根据mask ...

  3. Java并发编程 (四) 线程安全性

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.线程安全性-原子性-atomic-1 1.线程安全性 定义: 当某个线程访问某个类时,不管运行时环境 ...

  4. Java 第十一届 蓝桥杯 省模拟赛 最大的元素距离

    在数列 a_1, a_2, -, a_n中,定义两个元素 a_i 和 a_j 的距离为 |i-j|+|a_i-a_j|,即元素下标的距离加上元素值的差的绝对值,其中 |x| 表示 x 的绝对值. 给定 ...

  5. Java实现 LeetCode 665 非递减数列(暴力)

    665. 非递减数列 给你一个长度为 n 的整数数组,请你判断在 最多 改变 1 个元素的情况下,该数组能否变成一个非递减数列. 我们是这样定义一个非递减数列的: 对于数组中所有的 i (1 < ...

  6. Java实现蓝桥杯历届试题回文数字

    历届试题 回文数字 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 观察数字:12321,123321 都有一个共同的特征,无论从左到右读还是从右向左读,都是相同的.这样的数字叫做: ...

  7. Java实现 LeetCode 598 范围求和 II(最小值相乘)

    598. 范围求和 II 给定一个初始元素全部为 0,大小为 m*n 的矩阵 M 以及在 M 上的一系列更新操作. 操作用二维数组表示,其中的每个操作用一个含有两个正整数 a 和 b 的数组表示,含义 ...

  8. Java实现N*N矩阵旋转(360度)

    N*N矩阵旋转 Description 给你一个n*n的矩阵,你的任务是将它逆时针旋转角度d. [输入] 输入的第一个数为T,表示接下来有T组数据. 每组数据的格式如下: 第一行为两个整数n,d.1& ...

  9. java实现矩形区域的交和并

    在编写图形界面软件的时候,经常会遇到处理两个矩形的关系. 如图[1.jpg]所示,矩形的交集指的是:两个矩形重叠区的矩形,当然也可能不存在(参看[2.jpg]).两个矩形的并集指的是:能包含这两个矩形 ...

  10. Supervisor操作相关的进程

    Supervisor是用Python开发的一个客户机/服务器系统,允许用户监视和控制UNIX类操作系统上的多个进程. 功能:用于监听.启动.停止.重启一个或多个进程. 当Supervisor管理的进程 ...