Redis有序集合详解
有序集合和集合類似,只是說它是有序的,和無序集合的主要區別在于每一個元素除了值之外,它還會多一個分數。分數是一個浮點數,在 Java 中是使用雙精度表示的,根據分數,Redis 就可以支持對分數從小到大或者從大到小的排序。
這里和無序集合一樣,對于每一個元素都是唯一的,但是對于不同元素而言,它的分數可以一樣。元素也是 String 數據類型,也是一種基于 hash 的存儲結構。
集合是通過哈希表實現的,所以添加、刪除、查找的復雜度都是 0(1)。集合中最大的成員數為 2 的 32 次方減 1(40 多億個成員),有序集合的數據結構如圖所示。
有序集合是依賴 key 標示它是屬于哪個集合,依賴分數進行排序,所以值和分數是必須的,而實際上不僅可以對分數進行排序,在滿足一定的條件下,也可以對值進行排序。
Redis基礎命令
有序集合和無序集合的命令是接近的,只是在這些命令的基礎上,會增加對于排序的操作,這些是我們在使用的時候需要注意的細節。
下面講解這些常用的有序集合的部分命令。有些時候 Redis 借助數據區間的表示方法來表示包含或者不包含,比如在數學的區間表示中,[2,5] 表示包含 2,但是不包含 5 的區間。
Redis有序集合的部分命令
在對有序集合、下標、區間的表示方法進行操作的時候,需要十分小心命令,注意它是操作分數還是值,稍有不慎就會出現問題。
這里命令比較多,也有些命令比較難使用,在使用的時候,務必要小心,不過好在我們使用 zset 的頻率并不是太高,下面是測試結果——有序集合命令展示。
spring-data-redis對有序集合的封裝
在 Spring 中使用 Redis 的有序集合,需要注意的是 Spring 對 Redis 有序集合的元素的值和分數的范圍(Range)和限制(Limit)進行了封裝,在演示如何使用 Spring 操作有序集合前要進一步了解它的封裝。
先介紹一個主要的接口——TypedTuple,它不是一個普通的接口,而一個內部接口,它是 org.springframework.data.redis.core.ZSetOperations 接口的內部接口,它定義了兩個方法,代碼如下所示。
public interface ZSetOperations<K,V>{...... public interface TypedTuple<V> extends Comparable<TypedTuple<V>< {V getValue();Double getScore(); } ...... }這里的 getValue() 是獲取值,而 getScore() 是獲取分數,但是它只是一個接口,而不是一個實現類。spring-data-redis 提供了一個默認的實現類—— DefaultTypedTuple,同樣它會實現 TypedTuple 接口,在默認的情況下 Spring 就會把帶有分數的有序集合的值和分數封裝到這個類中,這樣就可以通過這個類對象讀取對應的值和分數了。
Spring 不僅對有序集合元素封裝,而且對范圍也進行了封裝,方便使用。它是使用接口 org.springframework.data.redis.connection.RedisZSetCommands 下的內部類 Range 進行封裝的,它有一個靜態的 range() 方法,使用它就可以生成一個 Range 對象了,只是要清楚 Range 對象的幾個方法才行,為此我們來看看下面的偽代碼。
//設置大于等于min public Range gte(Object min) //設置大于min public Range gt(Object min) //設置小于等于max public Range lte(Object max) //設置小于max public Range lt(Object max)這 4 個方法就是最常用的范圍方法。下面討論一下限制,它是接口 org.springframework.data.redis.connection.RedisZSetCommands 下的內部類,它是一個簡單的 POJO,它存在兩個屬性,它們的 getter 和 setter 方法,如下面的代碼所示。
// ...... public interface RedisZSetCommands {// ...... public class Limit {int offset;int count; //setter和getter方法 } //...... }通過屬性的名稱很容易知道:offset 代表從第幾個開始截取,而 count 代表限制返回的總數量。
通過 Spring 操作有序集合
我們討論了 spring-data-redis 項目對有序集合的封裝。在測試代碼前,要把 RedisTemplate 的 keySerializer 和 valueSerializer 屬性都修改為字符串序列化器 StringRedisSerializer,測試代碼如下所示。
public static void testZset() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);// Spring提供接口 TypedTuple操作有序集合Set<TypedTuple> set1 = new HashSet<TypedTuple>();Set<TypedTuple> set2 = new HashSet<TypedTuple>();int j = 9;for (int i = 1; i <= 9; i++) {j--;// 計算分數和值Double score1 = Double.valueOf(i);String value1 = "x" + i;Double score2 = Double.valueOf(j);String value2 = j % 2 == 1 ? "y" + j : "x" + j;// 使用 Spring 提供的默認 TypedTuple--DefaultTypedTupleTypedTuple typedTuple1 = new DefaultTypedTuple(value1, score1);set1.add(typedTuple1);TypedTuple typedTuple2 = new DefaultTypedTuple(value2, score2);set2.add(typedTuple2);}// 將元素插入有序集合zset1redisTemplate.opsForZSet().add("zset1", set1);redisTemplate.opsForZSet().add("zset2", set2);// 統計總數Long size = null;size = redisTemplate.opsForZSet().zCard("set1");// 計分數為score,那么下面的方法就是求 3<=score<=6的元素size = redisTemplate.opsForZSet().count("zset1", 3, 6);Set set = null;// 從下標一開始截取5個元素,但是不返回分數,每一個元索是Stringset = redisTemplate.opsForZSet().range("zset1", 1, 5);printSet(set);// 截取集合所有元素,并且對集合按分數排序,并返回分數,每一個元素是TypedTupleset = redisTemplate.opsForZSet().rangeWithScores("zset1", 0, -1);printTypedTuple(set);// 將zset1和zset2兩個集合的交集放入集合inter_zsetsize = redisTemplate.opsForZSet().intersectAndStore("zset1", "zset2","inter_zset");// 區間Range range = Range.range();range.lt("x8");// 小于range.gt("x1"); // 大于set = redisTemplate.opsForZSet().rangeByLex("zset1", range);printSet(set);range.lte("x8"); // 小于等于range.gte("xl"); // 大于等于set = redisTemplate.opsForZSet().rangeByLex("zset1", range);printSet(set);// 限制返回個數Limit limit = Limit.limit();// 限制返回個數limit.count(4);// 限制從第五個開始截取limit.offset(5);// 求區間內的元素,并限制返回4條set = redisTemplate.opsForZSet().rangeByLex("zset1", range, limit);printSet(set);// 求排行,排名第1返回0,第2返回1Long rank = redisTemplate.opsForZSet().rank("zset1", "x4");System.err.println("rank = " + rank);// 刪除元素,返回刪除個數size = redisTemplate.opsForZSet().remove("zset1", "x5", "x6");System.err.println("delete = " + size);// 按照排行刪除從0開始算起,這里將刪除第排名第2和第3的元素size = redisTemplate.opsForZSet().removeRange("zset2", 1, 2);// 獲取所有集合的元素和分數,以-1代表全部元素set = redisTemplate.opsForZSet().rangeWithScores("zset2", 0, -1);printTypedTuple(set);// 刪除指定的元素size = redisTemplate.opsForZSet().remove("zset2", "y5", "y3");System.err.println(size);// 給集合中的一個元素的分數加上11Double dbl = redisTemplate.opsForZSet().incrementScore("zset1", "x1",11);redisTemplate.opsForZSet().removeRangeByScore("zset1", 1, 2);set = redisTemplate.opsForZSet().reverseRangeWithScores("zset2", 1, 10);printTypedTuple(set); }/** * 打印TypedTuple集合 * @param set * -- Set<TypedTuple> */ public static void printTypedTuple(Set<TypedTuple> set) {if (set != null && set.isEmpty()) {return;}Iterator iterator = set.iterator();while (iterator.hasNext()) {TypedTuple val = (TypedTuple) iterator.next();System.err.print("{value = " + val.getValue() + ", score = "+ val.getScore() + "}\n");} }/** * 打印普通集合 * @param set普通集合 */ public static void printSet(Set set) {if (set != null && set.isEmpty()) {return;}Iterator iterator = set.iterator();while (iterator .hasNext()) {Object val = iterator.next();System. out.print (val +"\t");}System.out.println(); }上面的代碼演示了大部分 Spring 對有序集合的操作,并給出了比較清晰的注釋,大家認真思考之后就能熟悉如何通過 Spring 操作有序集合了。
總結
以上是生活随笔為你收集整理的Redis有序集合详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Dev C++详细配置
- 下一篇: MySQL 过滤重复数据