Java 接入Redis: 列表集合与有序集合操作命令

Redis命令: list任务队列、set集合运算与zset分值排序

纲要

本文覆盖 Redis 剩下三类数据结构的命令:list 的顺序性与队列能力、set 的去重与集合运算、sorted set 的分值排序机制。

  • list 命令
    • LPUSH 头部插入、LRANGE 范围查询、RPOP 尾部弹出
    • LLEN 长度、BRPOP 阻塞式弹出
    • 左进右出实现 FIFO 任务队列
  • set 命令
    • SADD / SMEMBERS / SCARD
    • 集合运算:SINTER 交集、SUNION 并集、SDIFF 差集(顺序敏感
    • SREM 移除
  • sorted set 命令
    • ZADDscore 添加、ZRANGE 范围查询(可带 WITHSCORES
    • ZINCRBY 增加分值、ZREM 移除

list 类型命令

Redislist简单的字符串列表 ,每个元素都是 string 类型,按插入顺序排序。

命令 语法 作用
LPUSH LPUSH key value1 [value2 ...] 将一个或多个值插入列表头部(左侧)
LRANGE LRANGE key start stop 获取指定范围内的元素
RPOP RPOP key 移除并获取列表最后一个元素(右侧)
LLEN LLEN key 获取列表长度
BRPOP BRPOP key timeout 移除并获取最后一个元素,列表为空时阻塞

LPUSH 从左侧(头部)插入
list

d c b a
RPOP 从右侧(尾部)弹出

LPUSH 插入

bash 复制代码
127.0.0.1:6379> LPUSH mylist a b c
(integer) 3
127.0.0.1:6379> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"

注意顺序是 c b a 而非 a b ca 最先插入位于队尾,c 最后插入位于队头。

再插入一个元素:

bash 复制代码
127.0.0.1:6379> LPUSH mylist itcast
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "itcast"
2) "c"
3) "b"
4) "a"

itcast 最后插入,因此位于最头部(最左侧)。

允许重复元素

bash 复制代码
127.0.0.1:6379> LPUSH mylist a
(integer) 5
127.0.0.1:6379> LRANGE mylist 0 -1
1) "a"
2) "itcast"
3) "c"
4) "b"
5) "a"

列表中出现两个 a,说明 list 允许重复元素 ------这是它与 set 的关键差异。

LRANGE 范围说明

参数 含义
start 起始下标,0 表示第一个元素
stop 结束下标,-1 表示最后一个元素

LRANGE key 0 -1 表示查询全部元素,是最常用的写法。

RPOP 弹出

bash 复制代码
127.0.0.1:6379> RPOP mylist
"a"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "a"
2) "itcast"
3) "c"
4) "b"

RPOP 返回队尾元素 a,同时把它从列表中移除 ,列表由 5 个元素变为 4 个,新的队尾变成 b

LLEN 长度

bash 复制代码
127.0.0.1:6379> LLEN mylist
(integer) 4

BRPOP 阻塞式弹出

BRPOP 中的 BBlocking。功能与 RPOP 相同,但当列表为空时会阻塞等待



无,超过 timeout
BRPOP mylist 10
列表是否为空
立即弹出队尾元素

命令结束
阻塞等待
等待期间有新元素
超时自动结束

返回 nil

bash 复制代码
# 列表中有元素:立即返回
127.0.0.1:6379> BRPOP mylist 10
1) "mylist"
2) "c"

# 列表已空:阻塞 10 秒后自动结束
127.0.0.1:6379> BRPOP mylist 10
(nil)
(10.05s)

返回值为一个二元数组:第一个元素是列表名,第二个是被弹出的元素值。末尾的 (10.05s) 显示阻塞耗时。

BRPOP 是任务队列的关键命令:消费者无需轮询,列表为空时挂起等待,有新任务立即被唤醒处理,既实时又节省 CPU。

set 类型命令

setstring 类型的无序集合 ,成员唯一,不允许重复。

命令 语法 作用
SADD SADD key member1 [member2 ...] 添加一个或多个成员
SMEMBERS SMEMBERS key 返回所有成员
SCARD SCARD key 返回成员数量
SINTER SINTER key1 key2 交集:两集合共有元素
SUNION SUNION key1 key2 并集:合并并去重
SDIFF SDIFF key1 key2 差集 :属于 key1 但不属于 key2 的元素
SREM SREM key member1 [member2 ...] 移除一个或多个成员

SADD 与无序性

bash 复制代码
127.0.0.1:6379> SADD myset a b c d
(integer) 4
127.0.0.1:6379> SMEMBERS myset
1) "a"
2) "d"
3) "c"
4) "b"

返回顺序为 a d c b,与插入顺序 a b c d 不一致,印证了 set无序性

自动去重

bash 复制代码
127.0.0.1:6379> SADD myset a b
(integer) 0
127.0.0.1:6379> SMEMBERS myset
1) "a"
2) "d"
3) "c"
4) "b"

重复添加 ab 后,集合仍是 4 个元素,返回值 0 表示没有新成员被加入

SCARD 成员数

bash 复制代码
127.0.0.1:6379> SCARD myset
(integer) 4

集合运算

先准备第二个集合:

bash 复制代码
127.0.0.1:6379> SADD myset2 a b x y
(integer) 4

此时:myset = {a, b, c, d}myset2 = {a, b, x, y}
集合
myset = {a b c d}
myset2 = {a b x y}
SINTER 交集

{a b}
SUNION 并集

{a b c d x y}
SDIFF myset myset2

{c d}

bash 复制代码
# 交集:两集合共有元素
127.0.0.1:6379> SINTER myset myset2
1) "a"
2) "b"

# 并集:合并后去重
127.0.0.1:6379> SUNION myset myset2
1) "a"
2) "b"
3) "c"
4) "d"
5) "x"
6) "y"

# 差集:myset 减 myset2
127.0.0.1:6379> SDIFF myset myset2
1) "c"
2) "d"

# 差集:myset2 减 myset(顺序调换,结果不同)
127.0.0.1:6379> SDIFF myset2 myset
1) "x"
2) "y"
运算 顺序是否影响结果 结果
SINTER 交集 共有元素
SUNION 并集 合并去重
SDIFF 差集 只从第一个集合中取,取其中不在后续集合中的元素

SDIFF 的顺序敏感性SDIFF A B 的数学含义是 A - B。结果元素必定来自第一个集合 A,且是 A 中不属于 B 的部分。调换顺序得到完全不同的结果,这是集合运算中最易出错的一点。

SREM 移除

bash 复制代码
127.0.0.1:6379> SREM myset a b c
(integer) 3
127.0.0.1:6379> SMEMBERS myset
1) "d"

一次性移除三个成员,集合中只剩 d

sorted set 类型命令

sorted setzset)也是 string 类型元素的集合且不允许重复,但与 set 的本质区别是:每个元素关联一个 double 类型的 score(分值),集合按 score 从小到大排序
list 排序依据

插入先后顺序
有序
sorted set 排序依据

score 分值大小

命令 语法 作用
ZADD ZADD key score1 member1 [score2 member2 ...] 添加一个或多个带分值的元素
ZRANGE ZRANGE key start stop [WITHSCORES] 按分值升序返回指定范围元素
ZINCRBY ZINCRBY key increment member 为指定成员的 score 加上增量
ZREM ZREM key member [member ...] 移除一个或多个成员

ZADD 与 ZRANGE

bash 复制代码
127.0.0.1:6379> ZADD myset3 10.0 a 9.0 b
(integer) 2
127.0.0.1:6379> ZRANGE myset3 0 -1
1) "b"
2) "a"

a 的分值是 10.0b9.0,输出 b a------按分值从小到大排序,与插入顺序无关。

再插入一个介于两者之间的元素:

bash 复制代码
127.0.0.1:6379> ZADD myset3 9.9 c
(integer) 1
127.0.0.1:6379> ZRANGE myset3 0 -1
1) "b"
2) "c"
3) "a"

c 的分值 9.9 介于 9.010.0 之间,因此排在中间,验证了排序依据确实是 score

WITHSCORES 带分值返回

bash 复制代码
127.0.0.1:6379> ZRANGE myset3 0 -1 WITHSCORES
1) "b"
2) "9"
3) "c"
4) "9.9000000000000004"
5) "a"
6) "10"

WITHSCORES 参数后,元素与分值交替 返回。注意 9.9 显示为 9.9000000000000004,这是 double 浮点数的精度表现,属于正常现象。

ZINCRBY 增加分值

bash 复制代码
127.0.0.1:6379> ZINCRBY myset3 20 b
"29"
127.0.0.1:6379> ZRANGE myset3 0 -1 WITHSCORES
1) "c"
2) "9.9000000000000004"
3) "a"
4) "10"
5) "b"
6) "29"

b 的分值由 9 增加 20 变为 29,成为最大值,因此从首位排到了末位。参数顺序为:增量在前,成员在后。

ZREM 移除

bash 复制代码
127.0.0.1:6379> ZREM myset3 b
(integer) 1
127.0.0.1:6379> ZRANGE myset3 0 -1
1) "c"
2) "a"

三类有序/去重结构对比

维度 list set sorted set
是否有序
排序依据 插入顺序 --- score 分值
是否允许重复 允许 不允许 不允许
核心命令前缀 L S Z
典型场景 任务队列 去重、集合运算 排行榜

完整可运行代码

java 复制代码
package com.itheima.test;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * 演示 Redis list、set、sorted set 三类命令在 Java 中的对应操作。
 */
@SpringBootTest
public class RedisListSetZSetCommandTest {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * list:左进右出实现 FIFO 队列,以及阻塞式弹出。
     */
    @Test
    public void testList() {
        ListOperations<String, Object> ops = redisTemplate.opsForList();

        // LPUSH 从左侧插入,最终顺序为 c b a
        ops.leftPushAll("mylist", "a", "b", "c");
        System.out.println("LRANGE -> " + ops.range("mylist", 0, -1));

        // 允许重复元素
        ops.leftPush("mylist", "a");
        System.out.println("允许重复 -> " + ops.range("mylist", 0, -1));

        // LLEN
        System.out.println("LLEN -> " + ops.size("mylist"));

        // RPOP 从右侧弹出
        System.out.println("RPOP -> " + ops.rightPop("mylist"));
        System.out.println("弹出后 -> " + ops.range("mylist", 0, -1));

        // BRPOP 阻塞式弹出,超时 10 秒
        Object blocked = ops.rightPop("mylist", 10, TimeUnit.SECONDS);
        System.out.println("BRPOP -> " + blocked);
    }

    /**
     * set:去重特性与集合运算(注意 SDIFF 的顺序敏感性)。
     */
    @Test
    public void testSet() {
        SetOperations<String, Object> ops = redisTemplate.opsForSet();

        ops.add("myset", "a", "b", "c", "d");
        // 重复添加,成员数不变
        ops.add("myset", "a", "b");

        System.out.println("SMEMBERS -> " + ops.members("myset"));
        System.out.println("SCARD -> " + ops.size("myset"));

        ops.add("myset2", "a", "b", "x", "y");

        System.out.println("SINTER 交集 -> " + ops.intersect("myset", "myset2"));
        System.out.println("SUNION 并集 -> " + ops.union("myset", "myset2"));
        System.out.println("SDIFF myset-myset2 -> " + ops.difference("myset", "myset2"));
        System.out.println("SDIFF myset2-myset -> " + ops.difference("myset2", "myset"));

        ops.remove("myset", "a", "b", "c");
        System.out.println("SREM 后 -> " + ops.members("myset"));
    }

    /**
     * sorted set:按 score 排序,支持分值增减。
     */
    @Test
    public void testZSet() {
        ZSetOperations<String, Object> ops = redisTemplate.opsForZSet();

        ops.add("myset3", "a", 10.0);
        ops.add("myset3", "b", 9.0);
        ops.add("myset3", "c", 9.9);

        System.out.println("ZRANGE(升序)-> " + ops.range("myset3", 0, -1));

        Set<ZSetOperations.TypedTuple<Object>> withScores =
                ops.rangeWithScores("myset3", 0, -1);
        if (withScores != null) {
            withScores.forEach(t -> System.out.println("  成员 " + t.getValue() + " 分值 " + t.getScore()));
        }

        // ZINCRBY:b 的分值加 20
        ops.incrementScore("myset3", "b", 20);
        System.out.println("加分后 -> " + ops.rangeWithScores("myset3", 0, -1));

        ops.remove("myset3", "b");
        System.out.println("ZREM 后 -> " + ops.range("myset3", 0, -1));
    }

    /**
     * 清理本文产生的测试数据。
     */
    @Test
    public void cleanUp() {
        redisTemplate.delete("mylist");
        redisTemplate.delete("myset");
        redisTemplate.delete("myset2");
        redisTemplate.delete("myset3");
        System.out.println("清理完成,剩余 key -> " + redisTemplate.keys("*"));
    }
}

工程目录结构

txt 复制代码
springdataredis_demo/
├── pom.xml
└── src/
    ├── main/
    │   ├── java/com/itheima/
    │   │   ├── App.java
    │   │   └── config/
    │   │       └── RedisConfig.java
    │   └── resources/
    │       └── application.yml
    └── test/
        └── java/com/itheima/test/
            └── RedisListSetZSetCommandTest.java

API 速览

Redis 命令 RedisTemplate 对应方法 说明
LPUSH key v... opsForList().leftPushAll(k, v...) 左侧批量插入
LRANGE key 0 -1 opsForList().range(k, 0, -1) 范围查询
RPOP key opsForList().rightPop(k) 右侧弹出
LLEN key opsForList().size(k) 列表长度
BRPOP key timeout opsForList().rightPop(k, timeout, unit) 阻塞式弹出
SADD key m... opsForSet().add(k, m...) 添加成员
SMEMBERS key opsForSet().members(k) 所有成员
SCARD key opsForSet().size(k) 成员数
SINTER k1 k2 opsForSet().intersect(k1, k2) 交集
SUNION k1 k2 opsForSet().union(k1, k2) 并集
SDIFF k1 k2 opsForSet().difference(k1, k2) 差集,顺序敏感
SREM key m... opsForSet().remove(k, m...) 移除成员
ZADD key s m opsForZSet().add(k, m, score) 带分值添加
ZRANGE key 0 -1 opsForZSet().range(k, 0, -1) 升序查询
ZRANGE ... WITHSCORES opsForZSet().rangeWithScores(k, 0, -1) 带分值查询
ZINCRBY key inc m opsForZSet().incrementScore(k, m, inc) 增加分值
ZREM key m opsForZSet().remove(k, m) 移除成员

官方文档

总结

本文三类命令各自围绕一个核心机制展开:list 的核心是顺序 + 允许重复LPUSHRPOP 分别从左右端操作,构成 FIFO 队列,而 BRPOP 的阻塞特性让消费者免于轮询;set 的核心是无序 + 唯一 ,真正的价值在 SINTER/SUNION/SDIFF 三种集合运算,其中 SDIFF 结果只取自第一个集合,参数顺序直接决定结果;sorted set 的核心是每个元素绑定一个 doublescore 并按其升序排列ZINCRBY 修改分值会实时改变排名,这是排行榜类需求的直接支撑。

相关推荐
Ivanqhz1 小时前
SVD++算法
java·服务器·网络·深度学习·神经网络
qq_2518364572 小时前
springboot vue3 开发实现 拼豆管理系统
java·开发语言·ai编程
caoerzhong2 小时前
JeeWMS 开源仓库管理系统全景解读:一套 Java WMS 如何把 WMS/OMS/BMS/TMS 装进同一个系统
java·开源
曹牧3 小时前
Java集合框架:List、ArrayList、Map 和 HashMap
java
顾城猿3 小时前
STOMP 协议
java
骇客野人3 小时前
测试环境MySQL迁移Vastbase(海量数据库)完整改造与落地实施方案
运维·服务器
+VX:Fegn08954 小时前
计算机毕业设计|基于springboot + vue旅游管理系统(源码+数据库+文档)
java·开发语言·vue.js·spring boot·课程设计
需要8264 小时前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
一 乐4 小时前
二手交易平台|基于springboot + vue二手交易平台(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序