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

简介: 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

相关文章
|
7月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
457 6
|
6月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
400 86
|
8月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
6月前
|
存储 消息中间件 NoSQL
Redis数据结构:别小看这5把“瑞士军刀”,用好了性能飙升!
Redis提供5种基础数据结构及多种高级结构,如String、Hash、List、Set、ZSet,底层通过SDS、跳表等实现高效操作。灵活运用可解决缓存、计数、消息队列、排行榜等问题,结合Bitmap、HyperLogLog、GEO更可应对签到、UV统计、地理位置等场景,是高性能应用的核心利器。
|
6月前
|
存储 缓存 NoSQL
Redis基础命令与数据结构概览
Redis是一个功能强大的键值存储系统,提供了丰富的数据结构以及相应的操作命令来满足现代应用程序对于高速读写和灵活数据处理的需求。通过掌握这些基础命令,开发者能够高效地对Redis进行操作,实现数据存储和管理的高性能方案。
196 12
|
6月前
|
存储 消息中间件 NoSQL
【Redis】常用数据结构之List篇:从常用命令到典型使用场景
本文将系统探讨 Redis List 的核心特性、完整命令体系、底层存储实现以及典型实践场景,为读者构建从理论到应用的完整认知框架,助力开发者在实际业务中高效运用这一数据结构解决问题。
|
6月前
|
存储 缓存 NoSQL
【Redis】 常用数据结构之String篇:从SET/GET到INCR的超全教程
无论是需要快速缓存用户信息,还是实现高并发场景下的精准计数,深入理解String的特性与最佳实践,都是提升Redis使用效率的关键。接下来,让我们从基础命令开始,逐步揭开String数据结构的神秘面纱。
|
10月前
|
存储 缓存 NoSQL
Redis中的常用命令-get&set&keys&exists&expire&ttl&type的详细解析
总的来说,这些Redis命令提供了处理存储在内存中的键值对的便捷方式。通过理解和运用它们,你可以更有效地在Redis中操作数据,使其更好地服务于你的应用。
557 17
|
9月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
436 3

热门文章

最新文章