Redis数据类型之列表List

想学习更多Redis相关知识,请点击右侧链接查看Redis学习笔记:点我查看

一、Redis列表类型介绍

在正式讲解Redis列表时,我们需要对“列表”这一概念做一个详细说明:

列表是一种可以存储多个数据的结构,

在内存中,列表的存储主要有两种方式:一种是通过数组实现的列表(例如:Java中的ArrayList),另一种是通过链表实现的列表(例如:Java中的LinkedList),两种方式各有优缺点:

优点 缺点
数组 数组元素在内存中是连续存储的,存储结构简单,访问速度块 1. 数组大小需要在创建过程中指定,不能动态调整
2. 插入和删除操作效率较低
3. 需要连续的内存空间
链表 1. 可以动态调整链表大小,内存利用率较高
2. 插入和删除操作较为灵活
3. 元素可以分布在内存的任何位置,不需要连续的内存空间
1. 访问元素较慢,查找元素需要从头遍历
2. 由于链表元素在内存中分布不连续,访问速度较慢
3. 每个元素需要额外的指针存储前一个或后一个节点,内存开销增加

首先,Redis列表(List)符合其本身和列表的基本特征:一个键(key)对应多个值(value)

其次,Redis的列表是通过链表实现的,这也就意味着Redis添加元素效率非常高,并且Redis添加元素的主要方式是向列表的头部或者尾部添加元素。

由于Redis列表本身是一个链表,它的缺点在于访问某个元素并没有这么快。因此,Redis列表适合于存储较少的数据。如果需要存储大量数据,并且需要快速访问,可以使用Sorted Set(ZSet)解决这一问题。

应用场景:

1. 记录用户在社交网络最新发送的内容,例如:微信公众号文章推送

2. 进程间通信,使用生产者-消费者模式。生产者将数据存放到列表,消费者取出数据并执行相应的处理操作。

二、Redis列表类型常用命令

1. (列表头部)从左向右依次将元素存入列表中

1
LPUSH key element [element ...]

假设向当前列表list的左侧添加三个元素e1、e2和e3,这个存储过程如下图所示:

LPUSH命令执行成功默认返回数据存入到列表后列表中元素的总数量,以下是LPUSH命令的使用演示:

2. (列表尾部)从右向左依次将元素存入到列表中:

1
RPUSH key element [element ...]

假设向当前列表list的右侧添加两个元素e1、e2,这个存储过程如下图所示:

LPUSH命令执行成功默认返回数据存入到列表后列表中元素的总数量,以下是RPUSH命令的使用演示:

3. 从左向右获取列表指定范围的元素:

1
LRANGE key start stop

和字符串类型类似,Redis列表中每一个元素也有索引。假设当前Redis列表长度是 list.length

从左到右计算出的索引值范围是$[0, list.length)$,索引最大值是 list.length - 1

Redis也可以从右向左计算索引,默认最右侧的索引值为-1,到最左侧索引值为$-list.length$,右侧可以得出的索引值范围是$[-list.length, -1]$。

假设当前key对应的列表中有8个元素,以下是对索引值范围的分析图:

我们在使用LRANGE命令时,start和stop可使用索引范围是$[0, list.length)$或者$[-list.length, -1]$,两个范围内的值可以混用。例如:start位置是0,stop表示列表最末尾的元素就可以使用-1表示,使用LRANGE获取整个列表的内容的执行命令是 LRNAGE key 0 -1

以下是LRANGE命令的使用:

4. 获取当前列表长度:

1
LLEN key

5. 从左侧弹出一个元素

1
LPOP key

默认返回的就是当前弹出元素的值:

以下是LPOP执行过程动画(橘黄色为弹出的元素):

6. 从右侧弹出一个元素

1
RPOP key

默认返回就是当前弹出元素的值:

以下是RPOP执行过程动画(橘黄色为弹出的元素):

7. 根据索引值获取列表指定元素(默认从0开始):

1
LINDEX key index

这里的index就是列表的索引值,它的范围和我们在前面探讨列表的索引值范围一样。假设列表的长度是$list.length$,那么从左到右索引范围:$[0, list.length-1)$,从右向左计算得出的范围是$[-list.length, -1]$。

以下是LINDEX命令的使用:

8. 删除列表中count个值为element的元素:

1
LREM key count element

这里count分成如下几种情况:

  • count > 0:从头到尾删除count个值为element的元素;
  • count = 0:删除列表中所有值为element的元素;
  • count < 0:从尾到头删除countge值尾element的元素;

如果当前的key不存在则视为空列表,此时执行LREM命令返回结果是0。

以下是LREM命令的使用:

9. 截取指定范围的值后,再赋值给当前的key:

1
LTRIM key start stop

这里start和stop的索引值范围和前面讨论的范围相同,需要保证start和stop在索引值范围内。

如果stop的值比当前列表长度大,那么在执行LTRIM操作时,得到的结果仍然是当前列表的尾部位置,即$[start, list.length)$。

以下是LTRIM命令的使用:

10. 当前列表右侧弹出一个元素,于此同时从另一个列表的左侧存入值:

1
RPOPLPUSH source destination

假设source列表中有如下元素:e1、e2、e3;destination列表有如下元素:e4、e5。

执行RPOPLPUSH后,source列表尾部元素e3弹出,然后马上在destination列表头部存入,此时得到的结果是:

  • source列表:e1、e2;
  • destination列表:e3、e4、e5;

以下是RPOPLPUSH命令演示:

下图演示了RPOPLPUSH命令如何将e3从source列表存入到destination列表中:

11. 为当前列表指定位置设置新值:

1
LSET key index element

12. 在列表某个已有值的前后再添加具体值:

1
LINSERT key <BEFORE | AFTER> pivot element

返回结果是当前列表的长度。

如果当前key对应的是空列表,那么此命令不会执行任何操作。

如果key存在,但是列表中不存在pivot的值,此时系统会提示错误信息。

以下是LINSERT命令的使用:

三、Redis列表类型的注意事项

1. 访问列表的头部和尾部的时间复杂度是$O(1)$,这也就意味着访问这两个位置的效率非常高。然而,在列表内部需要操作元素的命令时间复杂度一般是$O(n)$,例如:

  • LINDEX
  • LINSERT
  • LSET

2. Redis列表最多可以存储$2^{32}-1$个元素:

3. 如果列表为空,Redis会自动删除对应的key;如果执行添加操作时,如果key不存在,此时Redis会创建一个列表并向里面添加元素。


Redis数据类型之列表List
https://www.icode504.com/posts/91.html
作者
iCode504
发布于
2025年4月13日
许可协议