前言
Redis 中的 List 类型是一种灵活的数据结构,其功能类似于编程语言中的数组或顺序表,但其内部实现更接近于双端队列 (deque)。这使得在列表的头部和尾部进行元素的插入和删除操作都非常高效。
List 类型的主要特性包括:
-
有序性:列表中的元素是按照插入顺序排列的。
-
可重复性:与 Set 不同,List 允许存储重复的元素。
-
高效的头尾操作:通过 lpush、rpush、lpop 和 rpop 等命令,可以快速地在列表两端添加或移除元素,因此常被用作栈或队列。
Redis 的 List 类型是实现消息队列等模式的基石,在 Redis 早期版本中,它是构建消息队列的主要方式,后来 Redis 也引入了更为专业的 Stream 类型。
列表list相当于数组或者顺序表
redis的下标支持负数下标,getrange
右侧插入叫做rpush
,右侧删除叫做rpop
左侧插入叫做lpush
,左侧删除叫做lpop
list内部的结构(编码方式),并非是一个简单的数组,而是更接近于“双端队列(deque)”
-
列表中的元素是有序的
-
列表中区分获取和删除的区别
lindex能获取元素的值
lrem也能返回被删除的元素的值 -
列表中的元素是允许重复的
想hash这样的类型,field是不能重复的
当前list,头和尾都能高效的插入删除元素,就可以把这个list当成一个栈、队列来使用
redis有一个典型的应用场景,就是作为消息队列,最早的时候,就是通过list类型,后来redis又提供了一个stream类型
lpush、lrange
lpush key element [element ...]
lpush
一次可以插入一个元素,也可以插入多个元素
按照顺序进行头插元素
1、2、3、4
插入完成之后,4是最前面的
时间复杂度是O(1)
返回的是list的长度
如果key已经存在,并且key对应的value类型不是list的话,此时lpush就会报错
redis中所有的数据类型的操作都是这样的,必须保证类型匹配
第一次返回长度为4,第二次为8
插入完成之后使用lrange
查看我们list的内容
lrange key start stop
后面我们需要指定一段区间的,也是闭区间的,下标也是支持负数的
使用命令就能查看整个list的内容了
lrange key 0 -1
如果给到的下标超出了范围会出现什么情况呢?
redis的做法,是直接尽可能的获取到给定区间的元素,如果给定区间非法,比如超出下标,尽可能获取到内容
lpushx、rpush、rpushx
lpushx:如果key存在时,将一个或者多个元素从左侧放入(头插)到list中。不存在,直接返回
lpushx key element [element...]
时间复杂度是O(1)
返回值是插入后list的长度
rpush将一个或者多个元素从右侧插入到list中
rpush key element [element...]
rpushx:如果key存在时,将一个或者多个元素从右侧放入(尾插)到list中。不存在,直接返回
rpushx key element [element...]
lpop、rpop
lpop:返回值是取出的元素或者nil,时间复杂度是O(1)
lpop key
rpop:从尾部删除数据,返回的即是删除的元素或者nil
rpop key [count]
可以发现这里是一个count的,说明我们可以指定删除数据的个数的
lpop是不支持后面加上个数的,但是rpop是支持的
lindex、linsert、llen
index:给定下标,获取到对应的元素
时间复杂度是O(N),N指的是list中元素个数
如果下标非法的话,会返回一个nil空值
lindex key index
下标是从0开始的,并且支持负数下标
linsert:在指定位置插入元素
时间复杂度是O(N) N表示列表的长度
返回值:插入后的list长度
linsert key <before|after> pivot element
before|after这两个选项是说明是插入到元素之前还是元素之后,这两个选项二选一即可
pivot就是插入元素的基准,是插入到这个元素的左侧还是右侧
element就是插入的元素
但是如果这个基准值如果存在多个的话如何
下面这里只插入到了第一个4的前面
linsert进行插入的时候,要根据基准值,找到对应的位置,从左往右找,找到第一个符合基准值的位置即可
llen:获取list长度
时间复杂度O(1)
llen key
lerm
rem是remove的缩写
lrem key count element
count表示元素的个数
element表示的是删除元素的值
时间复杂度是O(N+M)
N是list的长度,M是多少个元素要被删除,就是count的大小
-
如果count大于0的话,我们在删除这个element的时候是从左往右进行元素的查找的
如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key 2 1
,找到1,删除两次,所以就从左往右查找1,删除两个1即可
-
如果count<0的话,我们在删除这个element的时候是从右往左进行元素的查找的
如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key -2 1
,找到1,删除两次,所以就从右往左查找1,删除两个1即可
-
如果count=0的话,我们得删除所有对应的元素,
如果此时的list是1 2 3 4 1 2 3 4 1 2 3 4 的话,我们lrem key 0 1
,找到1,将list中所有的1删除了
ltrim 、lset
ltrim这个也是删除list中元素的命令,相较于lrem更加粗暴,
指定一个范围,范围内的保留,范围外的删除
时间复杂度是O(N)
ltrim key start stop
保留start和stop之间区间内的元素(区间外面两边的元素就直接被删除了)
比如说这里将中间的3 4 5 6 都保存了,其他的都删除了
lset:根据下标修改元素
时间复杂度O(N)
lset key index element
如果下标超过了范围的话,他是会报错提醒的
lindex可以很好的处理下标越界的情况,直接返回nil
lset来说则会报错的
blpop、brpop
blpop和brpop是lpop和rpop的阻塞版本,和对应非阻塞版本的作用基本一致
- 在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会理解返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间,期间 Redis 可以执⾏其他命令,但要求执⾏该命令的客⼾端会表现为阻塞状态
- 命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元素,命令⽴即返回。
- 如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素。
阻塞:当前的线程不走了,代码不继续执行了
会在满足一定的条件下,被唤醒
这里的两个命令的前缀b就是block的意思
redis中的list也相当于阻塞队列一样
线程安全,则只支持“队列为空”的情况,不考虑“队列满”
如果list中存在元素,blpop和brpop就和lpop以及rpop作用相同
如果list为空的话,blpop和brpop就会产生阻塞,直到队列不空为止
使用blpop和brpop的时候,可以显示设置阻塞时间的,阻塞期间redis是可以执行其他的命令的
blpop和brpop并不会对redis服务器产生影响,只是看起来耗时
blpop和brpop都是可以去尝试获取多个key的列表的元素的
多个Key对应多个list的,哪个有元素了,就会返回哪个元素
如果多个客户端同时多一个键执行pop,则最先执行命令的客户端会得到弹出的元素
blpop key [key…]timeout
可以指定一个key或者是多个key
每个key都对应任何一个非空,blpop都能把这里的元素给获取到,立即返回
如果这些list为空的话,此时就需要阻塞等待了,等待其他客户端往这些list中插入元素了
次数还能指定超时时间,单位是秒
1)针对一个非空list进行操作
返回的结果相当于是一个pair
一方面告诉我们当前的数据来自于哪个key,另一个方面会告诉我们取到的数据是啥
2)针对一个空链表进行操作
3)针对多个Key进行操作
brpop效果和blpop完全于一样
这两个阻塞命令主要用途就是来作为消息队列
命令总结
好的,这是根据您提供的图片生成的表格:
操作类型 | 命令 | 时间复杂度 | |
---|---|---|---|
添加 | rpush key value [value ...] | O(k), k 是元素个数 | |
lpush key value [value ...] | O(k), k 是元素个数 | ||
`linsert key before | after pivot value` | O(n), n 是 pivot 距离头尾的距离 | |
查找 | lrange key start end | O(s+n), s 是 start 偏移量, n 是 start 到 end 的范围 | |
lindex key index | O(n), n 是索引的偏移量 | ||
llen key | O(1) | ||
删除 | lpop key | O(1) | |
rpop key | O(1) | ||
lrem key count value | O(k), k 是元素个数 | ||
ltrim key start end | O(k), k 是元素个数 | ||
修改 | lset key index value | O(n), n 是索引的偏移量 | |
阻塞操作 | blpop brpop | O(1) |
list类型的应用场景
1、用list作为数组这样的结构来存储多个元素
2、生产消费模型
3、redis分频道阻塞消息队列模型
总结
本文详细介绍了 Redis 的 List(列表)数据类型,它是一个有序且允许元素重复的集合,底层实现为双端队列,保证了在头尾两端进行操作的效率。
我们探讨了 List 的核心操作命令,可以分为以下几类:
-
添加操作:lpush/rpush 用于从左侧或右侧插入一个或多个元素,linsert 可在指定元素前后插入新元素。
-
查找操作:lrange 用于获取指定范围内的元素,lindex 获取指定下标的元素,而 llen 则返回列表的长度,时间复杂度为 O(1)。
-
删除操作:lpop/rpop 分别从左侧或右侧移除一个元素,lrem 可以根据值删除指定数量的元素,ltrim 则通过保留指定范围内的元素来裁剪列表。
-
修改操作:lset 用于更新指定下标的元素值。
-
阻塞操作:blpop/brpop 是 lpop/rpop 的阻塞版本。当列表为空时,它们会阻塞客户端连接,直到有新元素可供弹出或超时,这使它们非常适合实现消息队列。
最后,我们介绍了 List 类型的典型应用场景,如作为普通数组存储数据,以及在生产者-消费者模型中充当高效、可靠的消息队列。