Redis-06Redis数据结构--集合Set

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Redis-06Redis数据结构--集合Set

概述


Redis 的集合不是一个线性结构,而是一个哈希表结构,它的内部会根据 hash 分子来存储和查找数据,理论上一个集合可以存储2的32次方减一(约42亿)个元素。


因为采用哈希表结构,所以对于 Redis 集合的插入、删除和查找的复杂度都是 0(1),只是我们需要注意 3 点

  1. 对于集合而言,它的每一个元素都是不能重复的,当插入相同记录的时候都会失败
  2. 集合是无序的
  3. 集合的每一个元素都是 String 数据结构类型


常用集合命令


官网: https://redis.io/commands#set



20190109233114273.png


Redis 的集合可以对于不同的集合进行操作,比如求出两个或者以上集合的交集、 差集和并集等


image.png

image.png


上述命令的前缀都包含 了 一个 s,用来表达这是集合的命令 , 集合是无序的 , 并且支持并集 、 交集和差集的运算。

下面通过命令行客户端来演示这些命令

127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> SADD set1 v1 v2 v3 v4 v5 v6
(integer) 6
127.0.0.1:6379> SADD set2 v0 v2 v4 v6 v8
(integer) 5
127.0.0.1:6379> SCARD set1
(integer) 6
127.0.0.1:6379> SDIFF set1 set2
1) "v5"
2) "v1"
3) "v3"
127.0.0.1:6379> SINTER set1 set2
1) "v4"
2) "v6"
3) "v2"
127.0.0.1:6379> SISMEMBER set2 v2
(integer) 1
127.0.0.1:6379> SISMEMBER set1 v2
(integer) 1
127.0.0.1:6379> SISMEMBER set v2
(integer) 0
127.0.0.1:6379> SMEMBERS set2
1) "v4"
2) "v8"
3) "v6"
4) "v0"
5) "v2"
127.0.0.1:6379> SPOP set1
"v5"
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v6"
3) "v1"
4) "v2"
5) "v3"
127.0.0.1:6379> SRANDMEMBER set1 2
1) "v4"
2) "v3"
127.0.0.1:6379> SREM set1 v1
(integer) 1
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v6"
3) "v2"
4) "v3"
127.0.0.1:6379> SUNION set1 set2
1) "v4"
2) "v8"
3) "v6"
4) "v0"
5) "v2"
6) "v3"
127.0.0.1:6379> SMEMBERS set1
1) "v4"
2) "v6"
3) "v2"
4) "v3"
127.0.0.1:6379> SMEMBERS set2
1) "v4"
2) "v8"
3) "v6"
4) "v0"
5) "v2"
127.0.0.1:6379> 


交集、并集和差集保存命令的用法

127.0.0.1:6379> FLUSHDB
OK
127.0.0.1:6379> SADD set1  v1 v2 v3 v4 v5 v6
(integer) 6
127.0.0.1:6379> SADD set2 v2 v4 v6 v8 
(integer) 4
127.0.0.1:6379> SDIFFSTORE diff_set set1 set2 
(integer) 3
127.0.0.1:6379> SMEMBERS diff_set
1) "v5"
2) "v1"
3) "v3"
127.0.0.1:6379> SUNIONSTORE union_set  set1 set2
(integer) 7
127.0.0.1:6379> SMEMBERS union_set
1) "v4"
2) "v8"
3) "v5"
4) "v1"
5) "v6"
6) "v3"
7) "v2"
127.0.0.1:6379> SINTERSTORE inter_set set1 set2
(integer) 3
127.0.0.1:6379> SMEMBERS inter_set
1) "v6"
2) "v2"
3) "v4"
127.0.0.1:6379> 


上述命令主要是求差集、并集和交集 , 并保存到新的集合中。


Spring中操作Redis 集合命令

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="classpath:redis/redis.properties" />
    <!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且没有maxWait属性,建议看一下Jedis源码或百度。 -->
    <!-- redis连接池配置 -->
    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!--最大空闲数 -->
        <property name="maxIdle" value="${redis.maxIdle}" />
        <!--连接池的最大数据库连接数 -->
        <property name="maxTotal" value="${redis.maxTotal}" />
        <!--最大建立连接等待时间 -->
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟) -->
        <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" />
        <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 -->
        <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" />
        <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 -->
        <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" />
      <property name="testOnBorrow" value="true"></property>
    <property name="testOnReturn" value="true"></property>
    <property name="testWhileIdle" value="true"></property>
    </bean>
  <!--redis连接工厂 -->
    <bean id="jedisConnectionFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
        destroy-method="destroy">
        <property name="poolConfig" ref="jedisPoolConfig"></property>
        <!--IP地址 -->
        <property name="hostName" value="${redis.host.ip}"></property>
        <!--端口号 -->
        <property name="port" value="${redis.port}"></property>
        <!--如果Redis设置有密码 -->
        <property name="password" value="${redis.password}" /> 
        <!--客户端超时时间单位是毫秒 -->
        <property name="timeout" value="${redis.timeout}"></property>
        <property name="usePool" value="true" />
        <!--<property name="database" value="0" /> -->
    </bean>
  <!-- 键值序列化器设置为String 类型 -->
  <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
  <!-- redis template definition -->
  <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
    p:connection-factory-ref="jedisConnectionFactory"
    p:keySerializer-ref="stringRedisSerializer"
    p:defaultSerializer-ref="stringRedisSerializer"
    p:valueSerializer-ref="stringRedisSerializer">
  </bean>
</beans>
package com.artisan.redis.baseStructure.set;
import java.util.List;
import java.util.Set;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
/**
 * 
 * 
 * @ClassName: SpringRedisSetDemo
 * 
 * @Description: 记得将 RedisTemplate 值序列化器设置为 StringRedisSerializer 然后运行该代码
 * 
 * @author: Mr.Yang
 * 
 * @date: 2018年9月26日 下午3:22:57
 */
public class SpringRedisSetDemo {
  private static final String SET1 = "set1";
  private static final String SET2 = "set2";
  public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/spring-redis-set.xml");
    RedisTemplate<String, String> redisTemplate = (RedisTemplate<String, String>) ctx.getBean("redisTemplate");
    Set<String> set = null;
    // 127.0.0.1:6379> FLUSHDB
    // OK
    // 127.0.0.1:6379> SADD set1 v1 v2 v3 v4 v5 v6
    // (integer) 6
    // 127.0.0.1:6379> SADD set2 v0 v2 v4 v6 v8
    // (integer) 5
    // 将元素加入列表
    redisTemplate.boundSetOps(SET1).add("v1", "v2", "v3", "v4", "v5", "v6");
    redisTemplate.boundSetOps(SET2).add("v0", "v2", "v4", "v6", "v8");
    // 127.0.0.1:6379> SCARD set1
    // (integer) 6
    // 求集合长度
    System.out.println(SET1 + "的长度为:" + redisTemplate.opsForSet().size(SET1));
    System.out.println(SET2 + "的长度为:" + redisTemplate.opsForSet().size(SET2));
    // 127.0.0.1:6379> SDIFF set1 set2
    // 1) "v5"
    // 2) "v1"
    // 3) "v3"
    // 求差集
    set = redisTemplate.opsForSet().difference(SET1, SET2);
    scanSet(set);
    // 求并集
    set = redisTemplate.opsForSet().intersect(SET1, SET2);
    scanSet(set);
    // 判断是否是集合中的元素
    boolean exist = redisTemplate.opsForSet().isMember(SET1, "v1");
    System.out.println(SET1 + "中存在v1:" + exist);
    // 获取集合所有元素
    set = redisTemplate.opsForSet().members(SET1);
    scanSet(set);
    set = redisTemplate.opsForSet().members(SET2);
    scanSet(set);
    // 从集合中随机弹出一个元素,集合中会删除该元素
    String randomValue = redisTemplate.opsForSet().pop(SET2);
    System.out.println(SET2 + "中弹出的随机元素为" + randomValue);
    System.out.println(SET2 + "的长度为:" + redisTemplate.opsForSet().size(SET2));
    // 随机获取一个集合的元素 ,该元素仍然在集合中
    randomValue = (String) redisTemplate.opsForSet().randomMember(SET1);
    System.out.println(randomValue);
    System.out.println(SET1 + "的长度为:" + redisTemplate.opsForSet().size(SET1));
    System.out.println("------------");
    // 随机获取集合中 的4 个元素
    List<String> list = redisTemplate.opsForSet().randomMembers(SET1, 4);
    for (String string : list) {
      System.out.println(string);
    }
    // 删除一个集合的元素,参数可以是多个
    Long value = redisTemplate.opsForSet().remove(SET1, "v1", "v2");
    System.out.println(SET1 + "中删除了" + value + "个元素");
    System.out.println(SET1 + "的长度为:" + redisTemplate.opsForSet().size(SET1));
    // 求两个集合的并集
    set = redisTemplate.opsForSet().union(SET1, SET2);
    scanSet(set);
    // 求两个集合的差集,并保存到集合 diff_set 中
    redisTemplate.opsForSet().differenceAndStore(SET1, SET2, "diff_set");
    scanSet(redisTemplate.opsForSet().members("diff_set"));
    // 求两个集合的交集,并保存到集合 inter_set 中
    redisTemplate.opsForSet().intersectAndStore(SET1, SET2, "inter_set");
    scanSet(redisTemplate.opsForSet().members("inter_set"));
    // 求两个集合的并集,并保存到集合 union_set 中
    redisTemplate.opsForSet().unionAndStore(SET1, SET2, "union_set");
    scanSet(redisTemplate.opsForSet().members("union_set"));
  }
  private static void scanSet(Set<String> set) {
    for (String value : set) {
      System.out.println(value);
    }
    System.out.println("----------------");
  }
}


输出

INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Wed Sep 26 16:18:55 CST 2018]; root of context hierarchy
INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-set.xml]
set1的长度为:6
set2的长度为:5
v5
v1
v3
----------------
v4
v6
v2
----------------
set1中存在v1:true
v4
v5
v1
v6
v2
v3
----------------
v4
v8
v6
v2
v0
----------------
set2中弹出的随机元素为v4
set2的长度为:4
v3
set1的长度为:6
------------
v1
v1
v4
v4
set1中删除了2个元素
set1的长度为:4
v4
v5
v8
v6
v0
v3
v2
----------------
v5
v3
v4
----------------
v6
----------------
v4
v5
v8
v6
v0
v3
v2
----------------



注意


使用 Spring 提供的 RedisTemplate 去展示多个命令可以学习到如何使用 RedisTemplate 操作 Redis 。 实际工作中并不是那么用的,因为每一 个操作会尝试从连接池里获取 一 个新的 Redis 连接,多个命令应该使用SessionCallback 接口进行操作 。


代码

代码托管到了 https://github.com/yangshangwei/redis_learn

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6天前
|
存储 索引 Python
Python常用数据结构——集合
Python常用数据结构——集合
21 3
|
5天前
|
存储 NoSQL Redis
6)深度解密 Redis 的集合(Set)
6)深度解密 Redis 的集合(Set)
14 1
|
8天前
|
存储 JSON NoSQL
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
这篇文章是关于Redis基本数据结构的学习笔记,包括了String、Hash、Set、List和SortedSet的介绍和常用命令。文章解释了每种数据结构的特点和使用场景,并通过命令示例演示了如何在Redis中操作这些数据结构。此外,还提供了一些练习示例,帮助读者更好地理解和应用这些数据结构。
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
|
18天前
|
存储 Java 程序员
【数据结构】初识集合&深入剖析顺序表(Arraylist)
Java集合框架主要由接口、实现类及迭代器组成,包括Collection和Map两大类。Collection涵盖List(有序、可重复)、Set(无序、不可重复),Map则由键值对构成。集合通过接口定义基本操作,具体实现由各类如ArrayList、HashSet等提供。迭代器允许遍历集合而不暴露其实现细节。List系列集合元素有序且可重复,Set系列元素无序且不可重复。集合遍历可通过迭代器、增强for循环、普通for循环及Lambda表达式实现,各有适用场景。其中ArrayList实现了动态数组功能,可根据需求自动调整大小。
29 11
|
2月前
|
存储 监控 NoSQL
redis数据结构-HyperLogLog
redis数据结构-HyperLogLog
34 1
|
2月前
|
存储 NoSQL 数据处理
redis数据结构-Bitmaps
redis数据结构-Bitmaps
30 0
|
5天前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
golang 栈数据结构的实现和应用
01_设计一个有getMin功能的栈
01_设计一个有getMin功能的栈
|
6天前
|
前端开发
07_用队列实现栈
07_用队列实现栈
06_用栈来求解汉诺塔问题
06_用栈来求解汉诺塔问题
下一篇
无影云桌面