继续补充基础数据类型List操作,
帮助读者系统学习List相关知识。
为帮助读者更加系统地学习Redis基础数据操作,
分享其他数据类型操作文章:
Redis进阶:图文讲解Redis底层数据结构之embstr,raw,ziplist,quicklist和hashtable (带源码讲解)
(1)文末附全部测试代码;
(2)本篇文章将学习使用如下函数(方法):
序号 | 操作 | method |
---|---|---|
1 | 新增 | lpush,rpush,linsert |
2 | 删除 | lpop,rpop,brpoplpush,ltrim |
3 | 修改 | lset |
4 | 查询 | llen,lrange,lindex,lpos |
Redis基础数据类型List代表存储的值(value)为列表。
本文采用连接池的方式直连Redis,连接池配置如下:
<dependency>
<groupId>redis.clientsgroupId>
<artifactId>jedisartifactId>
<version>3.5.1version>
dependency>
private static JedisPool getJedisPool() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// Jedis池:最大连接数
jedisPoolConfig.setMaxTotal(1);
// Jedis池:最大空闲连接数
jedisPoolConfig.setMaxIdle(10);
// Jedis池:等待时间
jedisPoolConfig.setMaxWaitMillis(3000);
// Jedis池:连接Redis超时时间
int connectTimeout = 2000;
String redisHost = "127.0.0.1";
int redisPort = 6379;
String redisPassword = "123456";
int redisDb = 0;
// 创建连接池
return new JedisPool(jedisPoolConfig, redisHost, redisPort, connectTimeout, redisPassword, redisDb);
}
List新增数据有三种情况,
Redis的列表类型数据按照左右两侧插入,
同时,支持在指定元素的前或者后面插入数据,
测试样例如下:
/**
* 新增数据.
* lpush:从头部新增:左侧进入列表
* rpush:从尾部新增:右侧进入列表
* linsert:指定数据前/后添加数据
*/
@Test
public void insertData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 从队列头部入队
Long res1 = jedis.lpush("namelist", "xiaoyi", "xiaoer", "xiaosan");
// 从队列尾部入队
Long res2 = jedis.rpush("namelist", "xiaosi", "xiaowu", "xiaoliu");
// 在给定数据后面插入数据
Long res3 = jedis.linsert("namelist", ListPosition.AFTER, "xiaoliu", "xiaox");
// 在给定数据前面插入数据
Long res4 = jedis.linsert("namelist", ListPosition.BEFORE, "xiaoliu", "xiaox");
logger.info(">>>>>>>List插入数据:{}, {}, {}, {}", res1, res2, res3, res4);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入List数据异常:", ex);
throw new RuntimeException(ex);
}
}
左侧插入数据源码如下图所示,
由注释可知,向list添加字符串数据,如果列表中键不存在则新建数据,如果键存在,但是,不是在list中,则返回错误。
时间复杂度:O(1)
list从右侧插入数据,其他同lrpush。
这个方法没有注释,不过,从使用结果来看,
是在某个元素的前面或后面插入数据。
List插入数据分左、右,
删除数据同样有左右之分,
同时,支持两个列表相互操作(brpoplpush)以及截取指定范围的数据,
测试样例如下:
/**
* 删除数据.
* lpop: 从,头部开始删除(列表左侧)
* rpop: 从,尾部开始删除(列表右侧)
* brpoplpush: 先删除列表A的数据,然后插入列表B,并返回删除的数据
* ltrim: 截取给定范围内的数据,删除范围外的数据
*/
@Test
public void deleteData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 从队列头部移除元素
String res1 = jedis.lpop("namelist");
// 从队列尾部移除元素
String res2 = jedis.rpop("namelist");
// 从namelist-1尾部删除数据并将该数据添加到namelist-2头部,返回该数据
String res3 = jedis.brpoplpush("namelist-1", "namelist-2", 10);
// 截取数据,保留指定区间的数据,保留0~5条数据
String res4 = jedis.ltrim("namelist", 0, 5);
logger.info(">>>>>>>List删除数据:{}, {}, {}, {}", res1, res2, res3, res4);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis list删除数据异常:", ex);
throw new RuntimeException(ex);
}
}
从左侧删除元素,
由注释可知,该方法为原子性方法。
从右侧删除数据,其他同lpop。
这个方法比较有意思,
从列表a移除元素,然后存入列表b,
两个操作,放在一个方法中。
之前的列表移除元素方法都是移除某一个元素,
当然,redis提供了批量操作,按照元素范围截取元素。
Redis修改数据,
测试样例如下:
/**
* 编辑数据.
* lset: 修改指定位置(索引)的数据
*/
@Test
public void editData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 修改指定位置数据
String res1 = jedis.lset("namelist", 0, "xiaoling");
logger.info(">>>>>>>List修改:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改数据异常:", ex);
throw new RuntimeException(ex);
}
}
修改数据源码如下所示,
由注释可知,在List指定位置使用新值覆盖旧值,
时间复杂度:O(n)。
查询数据有四种方式:查询数据个数、查询指定索引范围数据、查询某个索引位置的数据以及查询指定元素的索引。
测试样例如下:
/**
* 查询数据.
* llen: 查询列表存储数据个数
* lrange: 查询给定范围内(索引)的数据
* lindex: 查询指定位置(索引)的数据
* lpos: 查询指定元素的索引,取遇到的第一个元素索引
*/
@Test
public void queryData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 查询List元素个数
Long res1 = jedis.llen("namelist");
// 查询范围内数据
List<String> res2 = jedis.lrange("namelist", 0, 2);
// 查询指定位置数据
String res3 = jedis.lindex("namelist", 0);
// 查询元素所在位置,取遇到的第一个元素索引
Long res4 = jedis.lpos("namelist", "xiaoling");
logger.info(">>>>>>>List查询:{}, {}, {}, {}", res1, res2, res3, res4);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis list查询数据异常:", ex);
throw new RuntimeException(ex);
}
}
查询List长度源码如下图所示,
由注释可知,当列表为空或列表不存在返回0。
查询列表指定索引范围的元素源码如下图所示,
由注释可知,索引从0开始,查询给定范围的元素,返回列表。
查询某个索引的元素源码如下图所示,
由源码可知,索引0表示第一个元素,正数依次类推,
负数,-1表示最后一个元素,依次倒序类推。
查询某个元素的索引,源码如下图所示,
这个方法没有注释,按照测试的功能可知,
是获取某个元素的索引。
Redis列表操作总结:
序号 | 操作 | method |
---|---|---|
1 | 新增 | lpush,rpush,linsert |
2 | 删除 | lpop,rpop,brpoplpush,ltrim |
3 | 修改 | lset |
4 | 查询 | llen,lrange,lindex,lpos |
为帮助读者更加系统地学习Redis基础数据操作,
分享其他数据类型操作文章:
Redis进阶:图文讲解Redis底层数据结构之embstr,raw,ziplist,quicklist和hashtable (带源码讲解)
package database_test.redis_test;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ListPosition;
import java.util.List;
/**
* List测试.
*
* @author xindaqi
* @since 2022-08-16 9:43
*/
public class ListTest {
private static final Logger logger = LoggerFactory.getLogger(ListTest.class);
private static JedisPool getJedisPool() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// Jedis池:最大连接数
jedisPoolConfig.setMaxTotal(1);
// Jedis池:最大空闲连接数
jedisPoolConfig.setMaxIdle(10);
// Jedis池:等待时间
jedisPoolConfig.setMaxWaitMillis(3000);
// Jedis池:连接Redis超时时间
int connectTimeout = 2000;
String redisHost = "127.0.0.1";
int redisPort = 6379;
String redisPassword = "123456";
int redisDb = 0;
// 创建连接池
return new JedisPool(jedisPoolConfig, redisHost, redisPort, connectTimeout, redisPassword, redisDb);
}
/**
* 新增数据.
* lpush:从头部新增:左侧进入列表
* rpush:从尾部新增:右侧进入列表
* linsert:指定数据前/后添加数据
*/
@Test
public void insertData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 从队列头部入队
Long res1 = jedis.lpush("namelist", "xiaoyi", "xiaoer", "xiaosan");
// 从队列尾部入队
Long res2 = jedis.rpush("namelist", "xiaosi", "xiaowu", "xiaoliu");
// 在给定数据后面插入数据
Long res3 = jedis.linsert("namelist", ListPosition.AFTER, "xiaoliu", "xiaox");
// 在给定数据前面插入数据
Long res4 = jedis.linsert("namelist", ListPosition.BEFORE, "xiaoliu", "xiaox");
logger.info(">>>>>>>List插入数据:{}, {}, {}, {}", res1, res2, res3, res4);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis插入List数据异常:", ex);
throw new RuntimeException(ex);
}
}
/**
* 删除数据.
* lpop: 从,头部开始删除(列表左侧)
* rpop: 从,尾部开始删除(列表右侧)
* brpoplpush: 先删除列表A的数据,然后插入列表B,并返回删除的数据
* ltrim: 截取给定范围内的数据,删除范围外的数据
*/
@Test
public void deleteData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 从队列头部移除元素
String res1 = jedis.lpop("namelist");
// 从队列尾部移除元素
String res2 = jedis.rpop("namelist");
// 从namelist-1尾部删除数据并将该数据添加到namelist-2头部,返回该数据
String res3 = jedis.brpoplpush("namelist-1", "namelist-2", 10);
// 截取数据,保留指定区间的数据,保留0~5条数据
String res4 = jedis.ltrim("namelist", 0, 5);
logger.info(">>>>>>>List删除数据:{}, {}, {}, {}", res1, res2, res3, res4);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis list删除数据异常:", ex);
throw new RuntimeException(ex);
}
}
/**
* 编辑数据.
* lset: 修改指定位置(索引)的数据
*/
@Test
public void editData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 修改指定位置数据
String res1 = jedis.lset("namelist", 0, "xiaoling");
logger.info(">>>>>>>List修改:{}", res1);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis修改数据异常:", ex);
throw new RuntimeException(ex);
}
}
/**
* 查询数据.
* llen: 查询列表存储数据个数
* lrange: 查询给定范围内(索引)的数据
* lindex: 查询指定位置(索引)的数据
* lpos: 查询指定元素的索引,取遇到的第一个元素索引
*/
@Test
public void queryData() {
try (Jedis jedis = getJedisPool().getResource()) {
// 查询List元素个数
Long res1 = jedis.llen("namelist");
// 查询范围内数据
List<String> res2 = jedis.lrange("namelist", 0, 2);
// 查询指定位置数据
String res3 = jedis.lindex("namelist", 0);
// 查询元素所在位置,取遇到的第一个元素索引
Long res4 = jedis.lpos("namelist", "xiaoling");
logger.info(">>>>>>>List查询:{}, {}, {}, {}", res1, res2, res3, res4);
} catch (Exception ex) {
logger.error(">>>>>>>>Redis list查询数据异常:", ex);
throw new RuntimeException(ex);
}
}
}