Jedis 操作 Redis 数据结构全攻略

Hello , 大家好 , 这个专栏给大家带来的是 Redis 系列 !
我们之前主要学习的是各种 Redis 的基本操作所对应的命令 , 都是在 Redis 的客户端的命令行中手动执行的 , 但是这种方式并不是日常开发主要的形式 .
在日常工作中 , 最常见的就是用程序来操作 Redis , 利用 Redis 提供的 API 来实现一些功能 .

在这里插入图片描述

本专栏旨在为初学者提供一个全面的 Redis 学习路径,从基础概念到实际应用,帮助读者快速掌握 Redis 的使用和管理技巧。通过本专栏的学习,能够构建坚实的 Redis 知识基础,并能够在实际学习以及工作中灵活运用 Redis 解决问题 .
专栏地址 : Redis 入门实践

一 . 认识 RESP

那为什么我们通过代码就可以编写出一个自定义的 Redis 客户端呢 ?
我们先来回顾一个知识 , 在网络通信过程中 , 会用到很多的协议 , 从下往上的顺序依次是 :

  1. 物理层
  2. 数据链路层 : 以太网协议
  3. 网络层 : IP 协议
  4. 传输层 : TCP/UDP 协议
  5. 应用层 : 自定义协议 (比如 : HTTP 协议)

虽然业界有许多成熟的自定义协议 , 但是更多的时候 , 大家都会选择自定义应用层协议 , Redis 应用层的协议就是自定义的协议 .
那客户端按照应用层协议发送请求 , 服务器按照协议内容进行解析 , 再按照这个协议去构造响应 , 客户端去解析响应 . 那我们要想通信能够完成 , 就需要开发客户端和开发服务器的程序员都非常清楚协议的细节 .
那如果我们想要开发一个 Redis 客户端 , 也需要知道 Redis 的应用层协议 , 我们也可以从官网中获取到 Redis 的应用层协议 : https://redis.io/docs/reference/protocol-spec
image.png
那 RESP 就是 Redis 的自定义的应用层协议的名字
我们来阅读以下这篇文档
image.png
简单好实现 : Redis 的客户端使用一种叫做 RESP 的协议来去跟 Redis 服务器进行通讯 .
image.png
可以快速进行解析 : RESP 可以识别不同的类型 , 比如整型、字符串、数组 .
image.png
客户端和服务器是基于 TCP 协议进行连接的 , 但是其实跟 TCP 关系没多大 , 只是借助 TCP 这个平台
image.png
Redis 客户端请求和响应是一问一答类型的

客户端给服务器发送一个请求 , 服务器返回给客户端一个响应

image.png
客户端给服务器发送的是 Redis 命令 , 以 bulk strings 数组的形式发送的
那不同的命令返回结果也不同 , 有的命令直接返回 ok , 有的命令可能返回整数 …
image.png
image.png
如果是简单的字符串 , 用 ‘+’ 表示 , 如果是简单的错误 , 用 ‘-’ 表示 …
下面还介绍了一个样例 , 服务端返回 OK 时候的表示
image.png
那服务器返回 OK 就会返回这串字符串 , 将这串字符串写入到 TCP 的 socket 中

因此 , Redis 的客户端和服务器要做的工作 , 就是

  1. 按照上述格式构造出字符串 , 写入 socket 中
  2. 从 socket 中读取字符串 , 按照上述格式进行解析

那我们介绍 RESP 只是希望大家了解一下 Redis 底层是怎样通信的 , 并不影响后续代码的编写

二 . 前置操作

Redis 在官网上公开了自定义的应用层协议 : RESP , 那任何一个第三方都可以根据约定好的协议就可以实现出一个和 Redis 服务器通信的客户端程序 .
那也有很多大佬已经实现好了现成的客户端程序 , 我们可以直接调用 , 就不必再关心里面的细节了 .
在 Java 中 , 比较常用的是 Jedis , 因为他所提供的 API 和 Redis 的命令非常相似 , 十分好上手 .
那我们创建一个 Maven 项目 , 来去演示一系列的操作

2.1 创建项目

打开我们的 IDEA
image.png
image.png
image.png
然后在 pom.xml 中引入 Jedis 的依赖

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

image.png
然后我们就可以新建一个类 , 来去模拟 Jedis 的操作了
image.png
image.png

2.2 关于开放 Redis 端口的问题

我们开发代码 , 一般都使用 Windows 系统 , 但是我们的 Redis 是存在云服务器上的 , 要是使用 127.0.0.1 访问到云服务器上面的 Redis 肯定是实现不了的
❓ 那我们修改成云服务器的外网 IP 再去访问行不 ?

也是不可以的 , 我们 Redis 的默认端口 6379 默认是被云服务器的防火墙给保护起来的 .

❓ 那我们再把 6379 给放开不就好了 ?

不可以 ! 千万不要这样做 ! Redis 是非常容易被黑客攻击的 , Redis 的端口一旦公开到网上 , 就特别容易被入侵 !
虽然我们之前开放过 tomcat 的端口 , 这是因为 tomcat 端口的安全性非常高 . 但是 Redis 的 6379 端口 , 安全性非常差 , 一旦你开了 , 不出 3 天 , 服务器就要死翘翘 .
那我给 Redis 换个端口 , 是不是就安全了 ? -> 掩耳盗铃 , 还是不安全的 .

那我们有两种方案解决 :

  1. 直接让 Java 程序也在 Linux 上面运行 [不推荐]

需要我们把代码编写完毕之后 , 打成一个 jar 包 , 然后把 jar 包拷贝到云服务器上去执行
比较麻烦

  1. 配置 SSH 端口转发 , 把云服务器的 Redis 端口 , 映射到本地主机 [推荐]

2.2.1 端口转发?

我们的 Windows 主机和云服务器是通过 SSH 连接起来的
无标题.png
那一个 Linux 主机上 , 需要映射的 Windows 设备有很多 , SSH 也有可能需要来给多个端口传递数据 , 这个时候 , 为了区分不同的端口 , 往往会把服务器的端口在本地用另外一个端口来表示
image.png
这样就实现了本地和云服务器端口的对应
只要咱们进行简单的配置 , 后续就把服务器的端口当成一个本地端口来使用即可

2.2.2 端口配置

打开我们的 XShell
image.png
image.png

SSH 端口转发 = SSH = SSH 隧道

image.png
image.png

只有当 SSH 连接上了之后 , 端口转发才生效
那把 SSH 连接断开 , 端口转发自然就失效了

那我们现在就可以检验一下是否生效了
使用 netstat 命令查看本地 8888 端口是否被绑定
image.png
那当我们配置了端口转发之后 ,如果之前配置过了端口转发 , 一定要断开之前的连接 , 重新连接才能生效
那我们后续 , 在 Java 代码中 , 通过 127.0.0.1:8888 就可以操作到我们的云服务器的 Redis 了 , 同时 , 外面的客户端是无法直接访问云服务器的 6379 端口的


我们再介绍一下 XTerminal 的配置方法
image.png
image.png
image.png

2.3 连接到 Redis 服务器

要想连接到 Redis 的服务器 , 我们需要使用 JedisPool 这样的对象 . 在 JedisPool 的构造方法中 , 就需要指定我们需要连接的服务器地址

注意服务器地址应该设置成本机的环回 IP + 我们配置的端口映射

import redis.clients.jedis.JedisPool;

public class RedisDemo {
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        
    }
}

接下来我们就需要从 JedisPool 这个 Redis 连接池中取出一个连接 , 连接用完之后需要释放

所以我们可以直接选择 try-with-resources 这样的方式自动释放

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisDemo {
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {

        }
    }
}

那之后我们就可以使用 Redis 的各种命令了
我们以 Redis 中的 ping 命令来测试连接是否成功

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisDemo {
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            // Redis 的各种命令, 都可以对应到 Jedis 对象的各种方法
            
            // 验证 Redis 的连通性
            String pong = jedis.ping();
            System.out.println(pong);
        }
    }
}

注意 : 我们要确保云服务器的 Redis 的配置文件里面的配置是正确的
输入 cd /etc/redis/ , 然后用 vim 打开 redis.conf
image.png
判断这两个位置是否跟我的一致 , 不一致的话就要修改一下
image.png

控制台打印 PONG 代表连接成功
image.png

三 . 通用命令

3.1 set 和 get

我们先来模拟一下最基本的 get 和 set 的使用

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class RedisDemo {
    /**
     * 模拟 get 和 set 的使用
     */
    public static void test01(Jedis jedis) {
        System.out.println("模拟 get 和 set 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 set 的使用
        jedis.set("k1", "v1");
        jedis.set("k2", "v2");

        // 模拟 get 的使用
        String v1 = jedis.get("k1");
        String v2 = jedis.get("k2");
        System.out.println("value1 = " + v1);
        System.out.println("value2 = " + v2);
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }
}

那 set 命令还有一些其他版本
image.png
我们可以看到 , set 方法还可以有第三个参数
那我们就可以先创建一个 SetParams 对象 , 然后调用 ex / nx / xx 等方法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

public class RedisDemo {
    /**
     * 模拟 get 和 set 的使用
     */
    public static void test01(Jedis jedis) {
        System.out.println("模拟 get 和 set 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 set 的使用
        jedis.set("k1", "v1");
        jedis.set("k2", "v2");

        // 模拟 get 的使用
        String v1 = jedis.get("k1");
        String v2 = jedis.get("k2");
        System.out.println("value1 = " + v1);
        System.out.println("value2 = " + v2);

        // set 的其它参数
        SetParams params = new SetParams();
        params.ex(10);// 设置超时时间为 10s
        params.xx();// 存在才去修改(更新)
        // params.nx();// 不存在才去创建
        jedis.set("k1", "v3", params);// 此时 k1 存在, 可以被修改
        System.out.println(jedis.get("k1"));
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }
}

我们可以看一下运行结果
image.png

3.2 exists 和 del

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

public class RedisDemo {
    /**
     * 模拟 exists 和 del 的使用
     */
    public static void test02(Jedis jedis) {
        System.out.println("模拟 exists 和 del 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");
        jedis.set("k2", "222");

        // 判断对应的 key 是否存在
        boolean result = jedis.exists("k1");
        System.out.println("k1 是否存在 : " + result);// true

        // 删除对应键值对
        // del 方法返回值为 long, 代表删除成功的个数
        long result2 = jedis.del("k2");
        System.out.println("删除成功的个数 : " + result2);// 1

        // 再去判断 k2 是否存在
        boolean result3 = jedis.exists("k2");
        System.out.println("k2 是否存在 : " + result3);// false

        // 可以删除多个 key
        // 但是仍然只会返回删除成功的个数
        long result4 = jedis.del("k1", "k2", "k3");
        System.out.println("删除成功的个数 : " + result4);// 1

    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }
}

3.3 keys

我们之前学习 keys 命令的时候 , 他有很多 pattern .
那我们先来看一下最基本的情况 : keys * , 查询所有的 key

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

public class RedisDemo {
    /**
     * 模拟 keys 的使用
     */
    public static void test03(Jedis jedis) {
        System.out.println("模拟 keys 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");
        jedis.set("k2", "222");
        jedis.set("k3", "333");
        jedis.set("k4", "444");

        // 模拟 keys 的使用
        // 返回值是 Set 类型
        Set<String> keys = jedis.keys("*");// 相当于 keys *
        System.out.println(keys);
    }
    
    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }
}

运行结果 :
image.png
我们需要知道的是 , keys 命令的返回值是 Set 类型
image.png
之所以使用 Set 类型而不是 List 类型的原因是 : Redis 中要求 key 是不能重复的 , 并且对于 key 的先后顺序也并没有要求 , 所以使用 Set 类型会更合适一些

3.4 expire、ttl、type

我们先来看 ttl 和 expire 的使用方法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

public class RedisDemo {

    /**
     * 模拟 expire、ttl 的使用
     */
    public static void test04(Jedis jedis) {
        System.out.println("模拟 expire、ttl、type 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");

        // 设置过期时间
        jedis.expire("k1", 10);// 设置过期时间为 10s

        // 获取剩余时间
        // ttl 返回值为 long
        long time = jedis.ttl("k1");
        System.out.println("当前键值对剩余时间为 " + time);
        // 等待 3s
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 休眠 3s 之后继续获取剩余时间
        time = jedis.ttl("k1");
        System.out.println("当前键值对剩余时间为 " + time);
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }
}

我们可以看一下运行结果
image.png
这就代表我们 expire 设置生效了 , 并且休眠 3s 之后还能正确获取剩余时

那接下来我们再来看 type 命令

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Set;

public class RedisDemo {
    /**
     * 模拟 type 的使用
     */
    public static void test05(Jedis jedis) {
        System.out.println("模拟 type 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("k1", "111");// 设置成字符串类型
        String type1 = jedis.type("k1");
        System.out.println("当前数据的类型为: " + type1);

        jedis.lpush("k2","111","222","333");
        String type2 = jedis.type("k2");
        System.out.println("当前数据的类型为: " + type2);

        jedis.hset("k3","f1","111");
        String type3 = jedis.type("k3");
        System.out.println("当前数据的类型为: " + type3);

        jedis.sadd("k4","111","222","333");
        String type4 = jedis.type("k4");
        System.out.println("当前数据的类型为: " + type4);

        jedis.zadd("k5",10,"zhangsan");
        String type5 = jedis.type("k5");
        System.out.println("当前数据的类型为: " + type5);
    }

    public static void main(String[] args) {
        // 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 从 Redis 连接池中取出一个连接
        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }
}

我们可以看一下运行结果
image.png

三 . string 相关命令

我们给大家介绍这几个命令 :

  1. 针对多个 key 进行操作 : mset / mget
  2. 获取 / 设置字符串指定范围内容 : getrange / setrange
  3. 拼接字符串 : append
  4. 对数据进行自增自减 : incr / decr

3.1 mset / mget

注意 : mget 的返回值是 List , 而不是 Set
image.png

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // mget mset 的用法
    public static void test1(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 设置多个键值对
        jedis.mset("key1", "value1", "key2", "value2", "key3", "value3");

        // 3. 获取多个键值对
        // 返回值: List
        List<String> values = jedis.mget("key1", "key2", "key3");
        System.out.println("values : " + values);
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test1(jedis);
        }
    }
}

image.png
那我们的 value 与上面设置的 set 的顺序是一一对应的
那假如我们中间获取一个不存在的 key , 会发生什么 ?
image.png
如果获取不存在的值 , 就会打印 null , 这也说明了有几个参数 , 就有几个返回结果

3.2 getrange / setrange

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // getrange / setrange 的使用
    public static void test2(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 添加数据
        jedis.set("key", "HelloWorld!");

        // 3. 获取指定区间的顺序
        // 左闭右闭区间
        String result = jedis.getrange("key", 2, 5);
        System.out.println("result = " + result);

        // 4. 设置指定区间的值
        jedis.setrange("key", 2, "666");
        System.out.println("修改之后的值: " + jedis.get("key"));
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test2(jedis);
        }
    }
}

image.png

3.3 append

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // append 的使用
    public static void test3(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 模拟数据
        jedis.set("key", "Hello");

        // 3. 追加数据
        jedis.append("key", "World");

        // 4. 查看追加后的数据
        System.out.println("修改之后的值 : " + jedis.get("key"));
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test3(jedis);
        }
    }
}

image.png

3.4 incr / decr

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // incr / decr 的使用
    public static void test4(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 模拟数据
        // 这次要模拟一个整数的数据
        jedis.set("key", "100");

        // 3. 自增操作
        // 返回自增之后的值
        long result1 = jedis.incr("key");
        System.out.println("自增操作返回的值: " + result1);// 预期是 101

        // 4. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 101

        // 5. 自减操作
        long result2 = jedis.decr("key");
        System.out.println("自减操作返回的值: " + result2);// 预期是 100

        // 6. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 100
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test4(jedis);
        }
    }
}

image.png
那 incr 和 decr 还可以指定增加或者删除的值 , 使用 incrby 和 decrby

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class RedisDemoString {
    // incr / decr 的使用
    public static void test4(Jedis jedis) {
        // 1. 清空数据库
        // 避免上一组的测试数据影响到下一组的测试结果
        jedis.flushDB();

        // 2. 模拟数据
        // 这次要模拟一个整数的数据
        jedis.set("key", "100");

        // 3. 自增操作
        // 返回自增之后的值
        long result1 = jedis.incr("key");
        System.out.println("自增操作返回的值: " + result1);// 预期是 101

        // 4. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 101

        // 5. 自减操作
        long result2 = jedis.decr("key");
        System.out.println("自减操作返回的值: " + result2);// 预期是 100

        // 6. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 100

        // 7. 指定增加的值
        long result3 = jedis.incrBy("key", 10);
        System.out.println("指定增加的值最后变成了: " + result3);// 预期是 110

        // 8. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 110

        // 9. 指定减少的值
        long result4 = jedis.decrBy("key", 5);
        System.out.println("指定减少的值最后变成了: " + result4);// 预期是 105

        // 10. 获取 key 的值
        System.out.println("此时 key 的值为: " + jedis.get("key"));// 预期是 105
    }

    public static void main(String[] args) {
        // 1. 连接到 Redis 服务器上
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        // 2. 创建 Jedis 对象
        try (Jedis jedis = jedisPool.getResource()) {
            test4(jedis);
        }
    }
}

image.png

3.5 mset、mget

我们先来看 mset 和 mget 这一组
mset 可以设置多组键值对 , mget 可以获取多组键值对

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 mset mget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 mset 设置多组数据
        jedis.mset("k1","v1","k2","v2","k3","v3");

        // 模拟 mget 获取多组数据
        List<String> values1 = jedis.mget("k1","k2","k3");
        System.out.println("value 的值为 : " + values1);
    }
}

那我们来看一下运行结果
image.png
那要注意的是 , mget 的返回值是 List 而不是 Set
image.png

之前学习 keys 命令的时候 , 他的返回值是 Set , 是因为 key 要求不能重复 . 而我们的 value 是可以重复的 , 所以使用 List 来去接收

那如果我们获取的多个键值对中某个 key 不存在 , 该怎样返回呢 ?

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 mset mget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 模拟 mset 设置多组数据
        jedis.mset("k1","v1","k2","v2","k3","v3");

        // 模拟 mget 获取多组数据
        List<String> values1 = jedis.mget("k1","k2","k3");
        System.out.println("value 的值为 : " + values1);

        // 如果某个 key 在 Redis 中不存在
        // 则对应的位置用 null 表示
        List<String> values2 = jedis.mget("k1","k2","k100","k3");
        System.out.println("value 的值为 : " + values2);
    }
}

我们可以通过运行结果来看一下
image.png
那我们就知道了 , 如果某个 key 在 Redis 上不存在 , 则对应的 value 用 null 来表示

那我们再来看一下 getrange 和 setrange
getrange 的作用是获取到 [start , end] 区间内的数据
setrange 的作用是从 start 位置开始 , 修改内容为指定的字符串

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 getrange 和 setrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造测试数据
        jedis.set("key","abcdefghigklmn");

        // 获取 [start,end] 区间的数据
        String str1 = jedis.getrange("key",2,5);
        System.out.println(str1);

        // 从 start 位置开始, 修改内容为指定的字符串
        // 从下标为 2 的位置开始修改, 修改成 hahaha
        jedis.setrange("key",2,"hahaha");
        String str2 = jedis.get("key");
        System.out.println(str2);
    }
}

我们可以来看一下运行结果
image.png

3.6 append、incr、decr

append 的作用是拼接字符串

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 append 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("key", "Hello");

        // 拼接字符串
        jedis.append("key", "World");

        String value = jedis.get("key");
        System.out.println("value = " + value);
    }
}

我们可以看一下运行结果
image.png

我们再来看一下 incr 和 decr 的用法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class StringDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 incr 和 decr 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造数据
        jedis.set("key", "100");

        // 模拟 incr 的使用
        // incr 返回值类型为 long, 代表自增后的结果
        long result1 = jedis.incr("key");
        System.out.println("自增之后的值为 : " + result1);

        String value1 = jedis.get("key");
        System.out.println("value 的值为 : " + value1);

        // 模拟 decr 的使用
        // decr 返回值类型为 long, 代表自减后的结果
        long result2 = jedis.decr("key");
        System.out.println("自增之后的值为 : " + result2);

        String value2 = jedis.get("key");
        System.out.println("value 的值为 : " + value2);
    }
}

image.png
还有其他的比如 incrby、incrbyfloat 命令 , 我们就不进行演示了

四 . list 类型

4.1 lpush、lrange、rpush

lpush 的作用是向列表中添加元素 (头插法)
lrange 的作用是可以获取到列表中的元素
rpush 就相当于尾插法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 lpush 和 lrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 头插法向列表中插入元素
        // 返回值为插入成功之后 list 的长度
        jedis.lpush("key", "111", "222", "333");

        // 获取列表中的元素
        // 返回值为 List
        List<String> result1 = jedis.lrange("key", 0, -1);
        System.out.println("头插法之后的列表为 : " + result1);

        // 尾插法向列表中插入元素
        jedis.rpush("key", "555", "666");
        List<String> result2 = jedis.lrange("key", 0, -1);
        System.out.println("尾插法之后的列表为 : " + result2);
    }
}

我们可以看一下运行结果
image.png

4.2 lpop、rpop

lpop 就相当头头删法
rpop 就相当于尾删法

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 lpush 和 lrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造列表中的数据
        jedis.rpush("key", "111", "222", "333", "444");

        // 从列表尾部删除元素
        String result1 = jedis.rpop("key");
        System.out.println("尾删法删除的值为 : " + result1);
        result1 = jedis.rpop("key");
        System.out.println("尾删法删除的值为 : " + result1);

        // 从列表头部删除元素
        String result2 = jedis.lpop("key");
        System.out.println("头删法删除的值为 : " + result2);
        result2 = jedis.lpop("key");
        System.out.println("头删法删除的值为 : " + result2);

        // 此时列表为空
        // 继续获取就会获取到 null
        result1 = jedis.rpop("key");
        System.out.println("尾删法删除的值为 : " + result1);
        result2 = jedis.lpop("key");
        System.out.println("头删法删除的值为 : " + result2);
    }
}

image.png

4.3 blpop 和 brpop

blpop 和 brpop 就相当于阻塞版本的 lpop 和 rpop

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 blpop 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 我们先在列表为空的时候去执行以下 blpop
        // 第一个参数: 超时时间(单位: s)
        // 第二个参数: 要获取的 key
        // 返回值是一个二元组, 使用 List 接收
        // 一个是从哪个 key 获取到的结果, 一个是删除的元素是什么
        List<String> result = jedis.blpop(3, "key");
        System.out.println("从 " + result.get(0) + " 中获取到的结果");
        System.out.println("删除的元素为 : " + result.get(1));
    }
}

此时由于我们的 Redis 中并没有数据 , 所以 blpop 就会发生阻塞等待 , 超过设置的时间就会返回 null 导致代码出现空指针异常
image.png
那我们可以模拟一下其他客户端插入元素的情况
模拟 blpop 阻塞.gif

4.4 llen

llen 的作用是获取到列表的元素个数

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.List;

public class ListDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");
        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 llen 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造列表中的数据
        jedis.rpush("key", "111", "222", "333", "444");

        // 获取列表中的元素个数
        long len = jedis.llen("key");
        System.out.println("列表中的元素个数为 " + len);
    }
}

image.png

五 . set 类型

5.1 sadd、smembers

sadd 的作用是向集合中添加元素
smembers 是获取到集合中的元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 sadd、smembers 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // 获取集合中的元素
        // smembers 返回值是一个 Set
        // 注意: smembers 顺序是无序的, 与插入顺序无关
        Set<String> result = jedis.smembers("key");
        System.out.println("集合中的元素为 : " + result);
    }
}

image.png

5.2 sismember

sismember 的作用是判断元素是否在集合中存在

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 sismember 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // 判断元素是否存在
        // sismember 返回值为布尔类型
        boolean result1 = jedis.sismember("key", "111");
        System.out.println(result1);
        // key 不存在就会返回 false
        boolean result2 = jedis.sismember("key100", "111");
        System.out.println(result2);
        // value 不存在也会返回 false
        boolean result3 = jedis.sismember("key", "666");
        System.out.println(result3);
    }

}

image.png

5.3 scard

scard 是用来获取集合中的元素个数

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 scard 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // 获取集合中的元素个数
        long len = jedis.scard("key");
        System.out.println("集合中的元素个数为 : " + len);
    }
}

image.png

5.4 spop

spop 的作用是随机删除一个元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 spop 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key", "111", "222", "333", "444");

        // spop 的作用是随机删除一个元素
        // 返回值是被删除的元素
        String result = jedis.spop("key");
        System.out.println("随机删除的值为 : " + result);
    }
}

我们可以多次运行来看一下删除的值是否是固定的
spop 随机删除元素.gif

5.5 sinter、sinterstore

sinter 和 sinterstore 的作用是用来求多个集合的交集

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }

    private static void test05(Jedis jedis) {
        System.out.println("模拟 sinter、sinterstore 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向集合中添加元素
        jedis.sadd("key1", "111", "222", "333");
        jedis.sadd("key2", "111", "222", "444");

        // 求多个集合的交集
        Set<String> result1 = jedis.sinter("key1", "key2");
        System.out.println("sinter 交集的结果为 : " + result1);

        // 将交集的结果保存到另外的 key 中
        // sinterstore 的返回值表示交集的个数
        long len = jedis.sinterstore("dest", "key1", "key2");
        System.out.println("sinterstore 交集的长度为 : " + len);
        Set<String> result2 = jedis.smembers("dest");
        System.out.println("sinterstore 交集的结果为 : " + result2);
    }
}

image.png

六 . hash 类型

6.1 hset、hget

hset 的作用是向哈希中添加元素
hget 是用来获取哈希中的元素的
我们先来看一下 hset
image.png
他有两种版本 , 一种是正常添加 key-field-value , 另一种是将多组 field-value 打包成一个 Map 进行插入
我们分别来演示

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 hset 和 hget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加一组元素
        jedis.hset("k1", "f1", "111");

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // 获取哈希中的元素
        String result1 = jedis.hget("k1", "f1");
        System.out.println("哈希中的元素 : " + result1);
        // 如果 key 不存在, 就会获取失败
        String result2 = jedis.hget("k100", "f1");
        System.out.println("哈希中的元素 : " + result2);
        // 如果 field 不存在, 就会获取失败
        String result3 = jedis.hget("k1", "f100");
        System.out.println("哈希中的元素 : " + result3);
    }
}

image.png

6.2 hexists

hexists 的作用是用来判断元素是否存在

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 hexists 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f1", "111");
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // 判断某个元素是否存在
        // 返回值是布尔类型
        boolean result1 = jedis.hexists("k1", "f1");
        System.out.println("该元素是否存在 : " + result1);

        // 如果 key 不存在, 就会返回 false
        boolean result2 = jedis.hexists("k100", "f1");
        System.out.println("该元素是否存在 : " + result2);
        // 如果 field 不存在, 就会返回 false
        boolean result3 = jedis.hexists("k1", "f100");
        System.out.println("该元素是否存在 : " + result3);
    }
}

image.png

6.3 hdel

hedel 的作用是删除指定的 field

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.Map;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 hdel 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f1", "111");
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // hdel 的作用是删除指定的 key 下面的 field
        // 返回值代表删除成功的个数
        long delNum = jedis.hdel("k1", "f1", "f2", "f100");
        System.out.println("删除成功的个数为 : " + delNum);

        // 此时再去判断 f1 和 f2 是否还存在
        boolean result1 = jedis.hexists("k1", "f1");
        System.out.println("f1 是否存在 : " + result1);
        boolean result2 = jedis.hexists("k1", "f2");
        System.out.println("f2 是否存在 : " + result2);
    }
}

image.png

6.4 hkeys、hvals

hkeys 是用来获取哈希中所有的 key
hvals 是用来获取海中所有的 val

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 hkeys、hvals 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向哈希中添加多组元素
        // 将多组 field-value 打包成一个 Map
        Map<String, String> fields = new HashMap<>();
        fields.put("f1", "111");
        fields.put("f2", "222");
        fields.put("f3", "333");
        jedis.hset("k1", fields);

        // hkeys 用来获取所有的 field
        // 返回值是 Set 类型
        Set<String> result1 = jedis.hkeys("k1");
        System.out.println("keys : " + result1);

        // hvals 用来获取所有的 value
        // 返回值是 List 类型
        List<String> result2 = jedis.hvals("k1");
        System.out.println("vals : " + result2);
    }
}

image.png
那要注意的是 , hkeys 的返回值类型为 Set , hvals 的返回值类型为 List

这是因为哈希中要求 key 不能重复 , 所以使用 Set 来去接收 ; 而 value 是可以重复的 , 所以使用 List 来去接收

6.5 hmset、hmget

hmset 的作用与 hset 差不多 , 都是用来添加哈希的 , 并且添加多组哈希都需要用到 Map 辅助
hmget 的作用是获取多组哈希的值

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class HashDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }

    private static void test05(Jedis jedis) {
        System.out.println("模拟 hmset、hmget 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // hmset 可以添加多组哈希
        // 但仍然需要将多组 field-value 打包成一个 Map
        Map<String, String> map = new HashMap<>();
        map.put("f1", "111");
        map.put("f2", "222");
        map.put("f3", "333");
        jedis.hmset("k1", map);

        // hmget 可以获取多组哈希
        List<String> values = jedis.hmget("k1", "f1", "f2", "f3", "f4");
        System.out.println("values : " + values);
    }
}

image.png
那他的特点就是会根据 field 的顺序来去打印 value 的顺序 , 也就是说 field-value 是严格对应的

hkeys 和 hvals 则不是 , 他们都是随机打印的 , 并不能一一对应

image.png

七 . zset 类型

7.1 zadd、zrange

zadd 是向有序集合中添加元素
zrange 是用来获取有序集合的所有元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 zadd、zrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向 zset 中添加一组元素
        jedis.zadd("key", 10.0, "zhangsan");

        // 也可以向 zset 中添加多组元素
        // 但是也需要借助 Map 来去设置 score 和 value
        Map<String, Double> map = new HashMap<>();
        map.put("lisi", 20.0);
        map.put("wangwu", 30.0);
        jedis.zadd("key", map);

        // 获取 zset 中的元素
        // 返回值类型为 List
        List<String> members = jedis.zrange("key", 0, -1);
        System.out.println("有序集合中的元素为 : " + members);
    }
}

image.png
如果我们想既获取到 members , 又获取到 scores , 那就需要使用 zrangewithscores 这个方法了
但是 zrangewithscores 的返回值类型有些特殊 , 需要我们留意一下
image.png
那这个 Tuple 是 Jedis 提供给我们的数据类型 , 代表元组 (也就是一组数据)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test01(jedis);
        }
    }

    private static void test01(Jedis jedis) {
        System.out.println("模拟 zadd、zrange 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 向 zset 中添加一组元素
        jedis.zadd("key", 10.0, "zhangsan");

        // 也可以向 zset 中添加多组元素
        // 但是也需要借助 Map 来去设置 score 和 value
        Map<String, Double> map = new HashMap<>();
        map.put("lisi", 20.0);
        map.put("wangwu", 30.0);
        jedis.zadd("key", map);

        // 获取 zset 中的元素
        List<String> members = jedis.zrange("key", 0, -1);
        System.out.println("有序集合中的元素为 : " + members);

        // 既获取 members, 又获取 scores
        List<Tuple> memberswithscores = jedis.zrangeWithScores("key", 0, -1);
        System.out.println("有序集合中的元素为 : " + memberswithscores);

        // 获取其中某一个元素
        String member = memberswithscores.get(0).getElement();
        double score = memberswithscores.get(0).getScore();
        System.out.println("第一个元素 : " + member + " , 他的成绩为 : " + score);
    }
}

image.png

7.2 zcard

zcard 是用来统计有序集合的元素个数的

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test02(jedis);
        }
    }

    private static void test02(Jedis jedis) {
        System.out.println("模拟 zcard 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 获取有序集合的元素个数
        long len = jedis.zcard("key");
        System.out.println("有序集合的元素个数为 : " + len);
    }
}

image.png

7.3 zrem

zrem 用来删除指定元素

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test03(jedis);
        }
    }

    private static void test03(Jedis jedis) {
        System.out.println("模拟 zrem 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 删除指定元素
        // 可以删除多组数据, 返回值代表删除成功的个数
        long remNum = jedis.zrem("key", "zhangsan", "lisi");
        System.out.println("删除成功的元素个数 : " + remNum);

        // 获取剩余元素
        List<Tuple> result = jedis.zrangeWithScores("key", 0, -1);
        System.out.println("剩余元素 : " + result);
    }
}

image.png

7.4 zscore

zscore 的作用是根据 member 来去找到对应的 score

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 zscore 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 根据 member 获取对应的分数
        double score1 = jedis.zscore("key", "zhangsan");
        System.out.println("score = " + score1);
    }
}

image.png
但是有一个需要注意的位置 : 就是我们在接收 score 的时候 , 使用的是 double 来去接收的
如果 member 存在 , 那么正常返回成绩 , 没什么问题 ; 但是如果 member 不存在呢 ?
member 不存在 , zscore 就会返回 null , 用 double 去接收 null 是会触发空指针异常的 .
image.png
所以我们尽量谁用包装类来去接收

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test04(jedis);
        }
    }

    private static void test04(Jedis jedis) {
        System.out.println("模拟 zscore 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 根据 member 获取对应的分数
        // 注意: 请使用包装类进行接收, 避免触发空指针异常
        Double score1 = jedis.zscore("key", "zhangsan");
        System.out.println("score = " + score1);
    }
}

image.png

7.5 zrank

zrank 的作用是获取到某个 member 他所对应的排名 (下标)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.resps.Tuple;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ZSetDemo {
    public static void main(String[] args) {
        JedisPool jedisPool = new JedisPool("tcp://127.0.0.1:8888");

        try (Jedis jedis = jedisPool.getResource()) {
            test05(jedis);
        }
    }
    
    private static void test05(Jedis jedis) {
        System.out.println("模拟 zrank 的使用");

        // 避免上一组测试的残留数据影响到本组测试结果
        // 所以需要先清空一下数据库
        jedis.flushDB();

        // 构造多组有序集合数据
        jedis.zadd("key", 10, "zhangsan");
        jedis.zadd("key", 20, "lisi");
        jedis.zadd("key", 30, "wangwu");

        // 获取某个 member 的排名
        // 注意: 返回值用 Long 包装类接收, 避免空指针异常
        Long rank = jedis.zrank("key", "lisi");
        System.out.println("rank : " + rank);
    }
}

image.png


这篇文章知识含量超级大 , 如果对大家有帮助的话 , 还请一键三连~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加勒比海涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值