redis-06 五大基本类型之Set
redis-06 五大基本类型之Set
概述
Redis 中的集合最多存储 232 - 1 个元素,具备固有集合的性质(唯一性、无序性),Redis 中的集合是通过 Hash Table 数据结构实现的,增删查的时间复杂度都是 $O(1)$,其中最方便的一点还是支持集合间的运算。
Redis 的集合数据类型都不支持嵌套,集合里面不能存储集合类型数据,集合里面不能存储集合而只能存储字符串。
命令
增、删、查、判断是否存在
- sadd
- 增加元素,可以覆盖原来的元素
- 返回成功增加的元素的个数
- srem
- 删除指定元素
- 返回删除的元素的个数
- sismember
- 判断元素是否存在于集合中
- 返回是否存在
- smembers
- 查看所有元素
- 返回元素集合
@Test
public void basicOperationOnSet() {
long effected = j.sadd("numbers", "1", "2", "3");
assertEquals(3L, effected);
effected = j.sadd("numbers", "3", "4", "5");
assertEquals(2L, effected);
effected = j.srem("numbers", "1", "2");
assertEquals(2L, effected);
boolean rel = j.sismember("numbers", "1");
assertFalse(rel);
rel = j.sismember("numbers", "3");
assertTrue(rel);
Set<String> numbers = j.smembers("numbers");
assertEquals(3, numbers.size());
assertTrue(numbers.contains("3"));
assertTrue(numbers.contains("4"));
assertTrue(numbers.contains("5"));
}
集合运算
差
- sdiff
- 参数依次是 A、B、C 三个集合
- 结果是 A - B - C
- 返回运算结果集合,不存储
@Test
public void setDiff() {
// if you want to store the result into other key, just append "store" behind the command
long effected = j.sadd("setA", "1", "2", "3");
assertEquals(3L, effected);
effected = j.sadd("setB", "3", "4", "5");
assertEquals(3L, effected);
effected = j.sadd("setC", "2", "5", "6");
assertEquals(3L, effected);
// A - B
Set<String> diffs = j.sdiff("setA", "setB");
assertEquals(2, diffs.size());
assertTrue(diffs.contains("1"));
assertTrue(diffs.contains("2"));
// A - B - C
diffs = j.sdiff("setA", "setB", "setC");
assertEquals(1, diffs.size());
assertTrue(diffs.contains("1"));
}
交
- sinter
- 参数依次是 A、B、C 三个集合
- 结果是 $A \cap B \cap C$
- 返回运算结果集合,不存储
@Test
public void setInter() {
// if you want to store the result into other key, just append "store" behind the command
long effected = j.sadd("setA", "1", "2", "3");
assertEquals(3L, effected);
effected = j.sadd("setB", "3", "4", "5");
assertEquals(3L, effected);
effected = j.sadd("setC", "2", "3", "6");
assertEquals(3L, effected);
Set<String> inters = j.sinter("setA", "setB", "setC");
assertEquals(1, inters.size());
assertTrue(inters.contains("3"));
}
并
- sunion
- 参数依次是 A、B、C 三个集合
- 结果是 $A \cup B \cup C$
- 返回运算结果集合,不存储
@Test
public void setUnion() {
// if you want to store the result into other key, just append "store" behind the command
long effected = j.sadd("setA", "1", "2", "3");
assertEquals(3L, effected);
effected = j.sadd("setB", "3", "4", "5");
assertEquals(3L, effected);
effected = j.sadd("setC", "2", "3", "6");
assertEquals(3L, effected);
Set<String> union = j.sunion("setA", "setB", "setC");
assertNotNull(union);
assertEquals(6, union.size());
assertTrue(union.contains("6"));
assertTrue(union.contains("5"));
assertTrue(union.contains("4"));
assertTrue(union.contains("3"));
assertTrue(union.contains("2"));
assertTrue(union.contains("1"));
}
集合其它操作
- srandmember
- 随机获取一个集合元素
- 原集合不变
- 参数
- 正数表示返回对应个数的不相同的元素
- 复数表示返回的元素可以相同
- spop
- 随机弹栈一个元素
- 原集合改变
@Test
public void randomGetFromSet() {
long effected = j.sadd("setA", "1", "2", "3");
assertEquals(3L, effected);
String value = j.srandmember("setA");
assertTrue(value.equals("1") || value.equals("2") || value.equals("3"));
List<String> setA = j.srandmember("setA", 2);
assertEquals(2, setA.size());
String sPop = j.spop("setA");
assertNotNull(sPop);
long size = j.scard("setA");
assertEquals(2L, size);
}
实践
文章标签
一篇文章的所有标签都是互不相同的,而且展示时没有顺序,所以我们可以使用集合来存储文章的标签;
同样的,有时我们希望通过文章标签来找到文章,如果通过检索所有文章的标签来捡出指定标签的文章似乎有些太低效了,因为我们也可以创建标签的 key,然后在这个 key 下面存储不同的值。最后的示意图大致是这样的:
如果想要获取对应已经发布的指定标签的文章的 ID 只需要查看 tag 标签对应的已经发布的文章集合就可以了,如果想要查看拥有多个标签的文章只需要求交集即可。