Redis学习笔记之七:有序集合类型
来源:互联网 发布:黑马程序员java面试题 编辑:程序博客网 时间:2024/06/11 03:07
Redis最后一种类型是有序集合类型ZSet,即排序的Set,但又与Set不同的是,它比Set多一个字段分数(score)用于排序等操作,从这点来看,相当于Java中的TreeMap,但与Java的TreeMap不同的是,TreeMap排序是指定Comparator对象来排序,通过比较Key来排序,最后构成一棵树。而Redis的ZSet在存储结构上类似于Set。
1、设值/取值
使用ZADD指令进行设值。格式ZADD key score value1 score value2...,返回值是成功添加元素的个数。演示如下:score必须是字符串形式的数值类型
可以看到score必须是一个有效的float类型。而且score的值是可以重复的,但Key的值不能重复。从这点来看与散列类型很相似,只不过在添加时把Key-Value的顺序颠倒了,而且Value还必须是字符串形式的整数。
ZSCORE指令可以取出对应的值,如上图,取出了Redis中Key为myzset对应的有序集合中Key为b的score。
在Redis中+inf表示正无穷,-inf表示负无穷。
2、排序功能(1)
ZRANGE指令用于获取某一范围的元素并根据score进行从小到大的排序,如果分数一致,则会比较Key,按照字典排序。这个指令与LRANGE指令是一致的。
其中withscores表示是否显示分数,带上这个参数则单行为Key,双行为score。
ZREVRANGE与ZRANGE指令一致,可以看成reverse ZRANGE的缩写,所以这个指令是从大到小。
3、获取有序集合中元素的个数
指令ZCARD用于获取有序集合中元素的个数,想到了Set的SCARD吧,所以从指令就可以看出这两种数据类型很类似。
4、获取指定分数范围内元素的个数
ZCOUNT指令用于获取分数在某个范围内的元素个数,格式ZCOUNT zset minScore maxScore。
可以看到如果minScore大于maxScore是返回0的。
5、删除元素
ZREM指令用于删除元素,格式ZREM zset key1 key2.返回值为实际删除元素个数。
6、按照分数范围删除元素
ZREMRANGEBYSCORE指令用于删除某个分数范围的元素。
格式ZREMRANGEBYSCORE zset minScore maxScore.
可以看到如果minScore大于maxScore,则指令不会删除任何元素。
7、按照分数排名范围删除元素
ZREMRANGEBYRAMK指令用于删除分数从小打大排列的前几位元素。
格式ZREMRANGEBYRANK zset start stop。支持负索引,演示如下:
8、获取元素的排名
ZRANK指令用于获取分数从小到大元素的排名,而ZREVRANK则相反。
Redis的索引是从0开始的。
9、获取指定分数范围的元素。
ZRANGEBYSCORE指令用于获取指定分数范围的前多少个元素。
格式ZRANGEBYSCORE zset minScore maxScore [WITHSCORES] [limit offset count],演示如下:
获取分数从小到大排序,分数在[20 - 80]之间的元素,然后输出分数,并且只取索引从1开始的3个元素(索引从0开始)。
注意,ZRANGE与ZREVRANGE是取分数排序后相应索引位置的数据,0代表第一个,-1代表最右边的元素(负索引),没有像这个命令需要指定分数范围。但ZRANGE zset 0 -1 可以认为与ZRANGEBYSCORE zset -inf +inf limit 0 -1等同。同样minScore大于maxScore返回empty。如下图演示:
与之相对应的是ZREVRANGEBYSCORE指令,格式略有不同
ZREVRANGEBYSCORE zset maxScore minScore [withscores] [limit offset count],演示如下
这儿特别要注意的是,ZRANGE和ZREVRANGE指令后面跟的是索引,且是上包含[]关系,如ZRANGE zset 0 3,是取索引从0到3的元素,包括0和3.而ZRANGEBYSCORE后面是limit,例如ZRANGEBYSCORE .... limit 0 3, 表示的是从索引为0的位置开始,一共取3个元素,如果count为负数(这儿举的例子是3),则相当于从该索引后的所有元素都会被取出来。例子如下:
10、增加某个值的分数
ZINCRBY指令用于增加某个Key对应的Score,指令格式ZINCRBY zset score key。如果Key不存在则相当于zadd指令。如果在Redis中存储文章或者新闻的访问量,没访问一次访问量+1,那这个命令非常有用。演示如下:
11、有序集合之间的运算
指令ZINTERSTORE用于计算有序集合的交集并保存在目标集合内。
ZINTERSTORE desSet numberSets zset1 zset2 zset3 [WIGHTS weight1 weight2] [aggregate SUM|MIN|MAX]
会有numberSets个有序集合参与计算,会依次将zset1, zset2,zset3进行交集运算,参考Set的SINTER指令,然后将所得交集存放进desSet中,那所得的分数怎么办呢?后面的WEIGHTS权重会依次与前面的zset对应,然后zset的分数会乘以权重得到分数就是这个分数,然后根据SUM|MIN|MAX采取相应的操作,演示如下:
此外还有ZUNIONSTORE指令求并集操作。
Redis的Java SDK
测试代码如下(略长):
package org.yamikaze.redis.test;import org.junit.After;import org.junit.Before;import org.junit.Test;import redis.clients.jedis.Jedis;import redis.clients.jedis.Tuple;import redis.clients.jedis.params.sortedset.ZAddParams;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;import static org.junit.Assert.*;/** * 使用Redis的Java SDK测试Zset类型 * @author yamikaze */public class ZSetTest { private Jedis client; private static final String KEY = "myzset"; private String key1 = "key1"; private double score1 = 20; private Map<String, Double> map; @Before public void setUp() { client = MyJedisFactory.defaultJedis(); map = new HashMap<String, Double>(16); map.put(key1, score1); map.put("key2", 20d); map.put("key3", 30d); map.put("key4", 40d); map.put("key5", 50d); map.put("key6", 60d); } /** * 测试ZADD,返回值是插入有序集合元素的个数 * 由于SDK中含有ZAddParams对象参数。 * 这个参数会改变zadd指令的行为,且一旦绑定了ZAddParams对象就不能再绑定 * 所以分为4个测试方法测试ZADD */ @Test public void testZAddWithoutZAddParams() { long size = client.zadd(KEY, 20, key1); assertTrue(size == 1); //有相同的Key时不会做插入,会做更新 size = client.zadd(KEY, 30, key1); assertTrue(size == 0); //插入多个元素 size = client.zadd(KEY, map); assertTrue(size == map.size() -1); } /** * 这儿顺带将ZSCORE指令测试了 */ @Test public void testZAddWithZAddParams01() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //指定参数ZAddParams, 经过上面的运行key1对应的score为20 ZAddParams zp = ZAddParams.zAddParams(); //nx表示如果key1不存在则插入,所以下面score为20,没有变为30 zp.nx(); client.zadd(KEY, 30, key1, zp); double score = client.zscore(KEY, key1); assertTrue(score == 20); } @Test public void testZAddWithZAddParams02() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //指定参数ZAddParams, 经过上面的运行key1对应的score为20 ZAddParams zp = ZAddParams.zAddParams(); //xx表示如果key存在才作插入(更新),否则不做插入 zp.xx(); client.zadd(KEY, 30, "1234", zp); //不存在,为空,表示上面的语句没有做插入 //如果使用语句double score来接收结果,会抛出NPE //如果是double score声明记得在Test上面加上(expected = NullPointerException.class) Double score = client.zscore(KEY, "1234"); assertNull(score); } @Test public void testZAddWithZAddParams03() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //指定参数ZAddParams, 经过上面的运行key1对应的score为20 ZAddParams zp = ZAddParams.zAddParams(); //ch表示返回被修改的元素个数 zp.ch(); //score被更新 size = client.zadd(KEY, 50, key1, zp); assertTrue(size == 1); //返回0,既没有更新score,也没有插入 size = client.zadd(KEY, 50, key1, zp); assertTrue(size == 0); //插入新元素 size = client.zadd(KEY, 50, "12345", zp); assertTrue(size == 1); } /** * 测试ZCARD指令,返回有序集合的元素数 */ @Test public void testZCard() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); Long count = client.zcard(KEY); assertTrue(count.equals(size)); } /** * 测试ZCOUNT指令 * 用于返回某个分数范围内的元素个数 * 20 20 30 40 50 60(map中的分数) */ @Test public void testZCount() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //20~50之间的个数应该是map的size()-1, 要包括20与50 long count = client.zcount(KEY, 20d, 50d); assertTrue(count == map.size() -1); } /** * 测试ZRANGE与ZREVRANGE */ @Test public void testZRangeAndZRevRange() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); Set<String> zranges = client.zrange(KEY, 0, -1); Set<String> zrevranges = client.zrevrange(KEY, 0, -1); assertNotNull(zranges); assertNotNull(zrevranges); assertTrue(zranges.size() == zrevranges.size()); for(String key: zranges) { assertTrue(zrevranges.contains(key)); } for(String key: zrevranges) { assertTrue(zranges.contains(key)); } } /** * 测试ZRANGE与ZREVRANGE带有WITHSCORES参数 */ @Test public void testZRangeAndZRevRangeWithScores() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //ZRANGE与ZREVRANGE各取一半,map的size为6 Set<Tuple> zranges = client.zrangeWithScores(KEY, 0, 2); Set<Tuple> zrevranges = client.zrevrangeWithScores(KEY, 0, 2); assertNotNull(zranges); assertNotNull(zrevranges); assertTrue(zranges.size() == zrevranges.size()); assertTrue(zranges.size() + zrevranges.size() == map.size()); Set<String> keys = new HashSet<String>(8); isEquals(zranges, map, keys); isEquals(zrevranges, map, keys); assertTrue(keys.size() == map.size()); } private void isEquals(Set<Tuple> tuples, Map<String, Double> maps, Set<String> keys) { for(Tuple tuple : tuples) { String key = tuple.getElement(); double score = tuple.getScore(); double mapScore = map.get(key); assertTrue(map.containsKey(key)); assertTrue(score == mapScore); keys.add(key); } } /** * 测试ZRANGEBYSCORE和ZRAVRANGEBYSCORE */ @Test public void testZRangeByScore() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //SDK并没有提供带有Limit的方法,只有zrangeByScoreWithScores方法 Set<String> ranges = client.zrangeByScore(KEY, 20, 30); Set<String> revranges = client.zrevrangeByScore(KEY, 60, 40); assertNotNull(ranges); assertNotNull(revranges); assertTrue(ranges.size() == revranges.size()); for(String str: ranges) { assertFalse(revranges.contains(str)); } for(String str: revranges) { assertFalse(ranges.contains(str)); } } /** * 测试ZREM删除元素 */ @Test public void testZRem() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); long remSize = client.zrem(KEY, key1, "abc"); assertTrue(remSize == 1); } /** * 测试ZREMRANGEBYSCORE,按照分数范围删除元素 */ @Test public void testZRemRangeByScore() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //20 20 30 40 50 60,执行完以下语句集合中应该只剩下一个元素 long remSize = client.zremrangeByScore(KEY, 20, 50); assertTrue(remSize == map.size() - 1); long count = client.zcard(KEY); assertTrue(count == 1); } /** * 测试ZREMRANGEBYRANK,按照排名删除元素 */ @Test public void testZRemRangeByRank() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //删除 20 20 30 集合中还剩40 50 60 long remSize = client.zremrangeByRank(KEY, 0, 2); //如果集合中的元素本身就不足指定的个数,那么将会全部删除 assertTrue(remSize == 3); Double score = client.zscore(KEY, key1); assertNull(score); //删除最大的几个元素 size = client.zadd(KEY, map); assertTrue(size == 3); //从指定的位置开始删除,如果start指定为-1,已经是最后一个元素了,无法删除 remSize = client.zremrangeByRank(KEY, -2, -1); //如果集合中的元素本身就不足指定的个数,那么将会全部删除 assertTrue(remSize == 2); score = client.zscore(KEY, "key6"); assertNull(score); score = client.zscore(KEY, "key5"); assertNull(score); } /** * ZRANK获取元素的排名 */ @Test public void testZRank() { long size = client.zadd(KEY, map); assertTrue(size == map.size()); //ZRANK,从小到大的排名 从0开始 long rank = client.zrank(KEY, key1); assertTrue(rank == 0); //key1 与key2的分数一致,然后按照字典排序,所以key2为1 rank = client.zrank(KEY, "key2"); assertTrue(rank != 0); assertTrue(rank == 1); //ZRENRANK 从大到小的排名 rank = client.zrevrank(KEY, key1); assertTrue(rank == 5); //不存在的key的排名为null Long rank1 = client.zrank(KEY, "abc"); assertNull(rank1); } /** * 篇幅原因,运算指令略 */ @After public void tearDown() { if(client != null) { client.del(KEY); } }}
总结
参考资料
- Redis学习笔记之七:有序集合类型
- Redis 学习笔记(七)之 有序集合
- Redis 学习笔记(七)之 有序集合
- Redis学习笔记(六)类型之有序集合
- Redis学习笔记(七)——Redis常用命令入门——有序集合类型
- redis学习笔记五之基础命令—有序集合
- Redis之Sorted-sets 有序集合类型
- Redis系列学习笔记6 有序集合
- redis学习笔记五(有序集合)
- Redis数据类型之有序集合类型--Redis系列六
- Redis学习笔记(五)类型之集合
- Redis学习笔记之六:集合类型Set
- redis源码分析(七)、redis命令学习总结—Redis 有序集合(sorted set)
- Redis笔记7:数据类型之有序集合(sorted set)
- Redis基础之有序集合
- redis学习笔记(二)---集合类型
- redis 学习手册之有序集合数据类型sorted-sets操作
- redis数据类型(五)有序集合类型
- 音频硬件发展史,以及DSD是如何产生的 一起说一说音频
- saltstack搭建lnmp并直接上线(rpm)
- 五、public\protect\private的区别
- 论GD&T在壳体设计中的广泛应用
- 40个Java多线程问题总结
- Redis学习笔记之七:有序集合类型
- 树 DFS序 详解[完全版]
- 如果我的文章对你有帮助,请支持我的淘宝店,多谢:
- 【翻译】EAST: An Efficient and Accurate Scene Text Detector
- C homework3
- Spring MVC工作原理
- 第二天 工作总结
- a标签用于下载
- unity的public在属性面板修改的实时性