1 缘起

继续补充基础数据类型List操作,
帮助读者系统学习List相关知识。

为帮助读者更加系统地学习Redis基础数据操作,
分享其他数据类型操作文章:

序号文章
1String操作详解
2Hash操作详解
3ZSet操作详解
4Set操作详解

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

2 List测试

Redis基础数据类型List代表存储的值(value)为列表。
本文采用连接池的方式直连Redis,连接池配置如下:

  • 依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.1</version>
</dependency>
  • Redis连接池配置(可以根据需要设置为共用)
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);
    }

2.1 新增数据

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);
        }
    }

2.1.1 lpush

左侧插入数据源码如下图所示,
由注释可知,向list添加字符串数据,如果列表中键不存在则新建数据,如果键存在,但是,不是在list中,则返回错误。
时间复杂度:O(1)
在这里插入图片描述

2.1.2 rpush

list从右侧插入数据,其他同lrpush。
在这里插入图片描述

2.1.3 linsert

这个方法没有注释,不过,从使用结果来看,
是在某个元素的前面或后面插入数据。
在这里插入图片描述

2.2 删除数据

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);
        }
    }

2.2.1 lpop

从左侧删除元素,
由注释可知,该方法为原子性方法。
在这里插入图片描述

2.2.2 rpop

从右侧删除数据,其他同lpop。
在这里插入图片描述

2.2.3 brpoplpush

这个方法比较有意思,
从列表a移除元素,然后存入列表b,
两个操作,放在一个方法中。
在这里插入图片描述

2.2.4 ltrim

之前的列表移除元素方法都是移除某一个元素,
当然,redis提供了批量操作,按照元素范围截取元素。
在这里插入图片描述

2.3 修改数据

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);
        }
    }

2.3.1 lset

修改数据源码如下所示,
由注释可知,在List指定位置使用新值覆盖旧值,
时间复杂度:O(n)。
在这里插入图片描述

2.4 查询数据

查询数据有四种方式:查询数据个数、查询指定索引范围数据、查询某个索引位置的数据以及查询指定元素的索引。
测试样例如下:

    /**
     * 查询数据.
     * 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);
        }
    }

2.4.1 llen

查询List长度源码如下图所示,
由注释可知,当列表为空或列表不存在返回0。
在这里插入图片描述

2.4.2 lrange

查询列表指定索引范围的元素源码如下图所示,
由注释可知,索引从0开始,查询给定范围的元素,返回列表。
在这里插入图片描述

2.4.3 lindex

查询某个索引的元素源码如下图所示,
由源码可知,索引0表示第一个元素,正数依次类推,
负数,-1表示最后一个元素,依次倒序类推。
在这里插入图片描述

2.4.4 lpos

查询某个元素的索引,源码如下图所示,
这个方法没有注释,按照测试的功能可知,
是获取某个元素的索引。
在这里插入图片描述

3 小结

Redis列表操作总结:

序号操作method
1新增lpush,rpush,linsert
2删除lpop,rpop,brpoplpush,ltrim
3修改lset
4查询llen,lrange,lindex,lpos

为帮助读者更加系统地学习Redis基础数据操作,
分享其他数据类型操作文章:

序号文章
1String操作详解
2Hash操作详解
3ZSet操作详解
4Set操作详解

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);
        }
    }
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐