简体   繁体   English

是否有等效于 Redis 排序集 (zset) 的 Java 数据结构

[英]Is there a Java data structure equivalent to Redis sorted sets (zset)

Redis has a data structure called a sorted set. Redis 有一种数据结构,称为有序集。

The interface is roughly that of a SortedMap, but sorted by value rather than key.接口大致是 SortedMap 的接口,但按值而不是键排序。 I could almost make do with a SortedSet, but they seem to assume static sort values.我几乎可以使用 SortedSet,但它们似乎假定静态排序值。

Is there a canonical Java implementation of a similar concept?是否有类似概念的规范 Java 实现?

My immediate use case is to build a set with a TTL on each element.我的直接用例是在每个元素上构建一个带有 TTL 的集合。 The value of the map would be the expiration time, and I'd periodically prune expired elements.地图的值将是过期时间,我会定期修剪过期元素。 I'd also be able to bump the expiration time periodically.我还可以定期调整到期时间。

So... several things.所以……有几件事。

First, decide which kind of access you'll be doing more of.首先,确定您将更多地使用哪种访问。 If you'll be doing more HashMap actions (get, put) than accessing a sorted list, then you're better off just using a HashMap and sorting the values when you want to prune the collection.如果您将执行更多 HashMap 操作(获取、放置)而不是访问排序列表,那么您最好只使用 HashMap 并在您想要修剪集合时对值进行排序。

As for pruning the collection, it sounds like you want to just remove values that have a time less than some timestamp rather than removing the earliest n items.至于修剪集合,听起来您只想删除时间小于某个时间戳的值,而不是删除最早的 n 个项目。 If that's the case then you're better off just filtering the HashMap based on whether the value meets a condition.如果是这种情况,那么您最好仅根据值是否满足条件来过滤 HashMap。 That's probably faster than trying to sort the list first and then remove old entries.这可能比尝试先对列表进行排序然后删除旧条目要快。

Since you need two separate conditions, one on the keys and the other one on the values, it is likely that the best performance on very large amounts of data will require two data structures.由于您需要两个独立的条件,一个在键上,另一个在值上,因此在大量数据上的最佳性能可能需要两种数据结构。 You could rely on a regular Set and, separately, insert the same objects in PriorityQueue ordered by TTL.您可以依赖常规 Set 并分别在 PriorityQueue 中按 TTL 排序插入相同的对象。 Bumping the TTL could be done by writing in a field of the object that contains an additional TTL;可以通过写入包含附加 TTL 的对象字段来实现 TTL; then, when you remove the next object, you check if there is an additional TTL, and if so, you put it back with this new TTL and additional TTL = 0 [I suggest this because the cost of removal from a PriorityQueue is O(n)].然后,当你移除下一个对象时,你检查是否有额外的 TTL,如果有,你用这个新的 TTL 和额外的 TTL = 0 把它放回去 [我建议这样做,因为从 PriorityQueue 中移除的成本是 O( n)]。 This would yield O(log n) time for removal of the next object (+ cost due to the bumped TTLs, this will depend on how often it happens) and insertion, and O(1) or O(log n) time for bumping a TTL, depending on the implementation of Set that you choose.这将产生 O(log n) 时间来移除下一个对象(+ 由于碰撞的 TTL 产生的成本,这将取决于它发生的频率)和插入,以及 O(1) 或 O(log n) 的碰撞时间一个 TTL,取决于您选择的 Set 的实现。

Of course, the cleanest approach would be to design a new class encapsulating all this.当然,最简洁的方法是设计一个封装所有这些的新类。

Also, all of this is overkill if your data set is not very large.此外,如果您的数据集不是很大,那么所有这些都是多余的。

Have a look at ExpiringMap .看看ExpiringMap Guava's cache may work for your use case as well. Guava 的缓存也可能适用于您的用例。

You can implement it using a combination of two data structures.您可以使用两种数据结构的组合来实现它。 A sorted mapping of keys to scores.键到分数的排序映射。 And a sorted reverse mapping of scores to keys.以及分数到键的排序反向映射。

In Java, typically these would be implemented with TreeMap (if we are sticking to the standard Collections Framework).在 Java 中,通常这些将使用 TreeMap 实现(如果我们坚持标准的集合框架)。

Redis uses Skip-Lists for maintaining the ordering, but Skip-Lists and Balanced Binary Search Trees (such as TreeMap) both serve the purpose to provide average O(log(N)) access here. Redis 使用跳过列表来维护排序,但跳过列表和平衡二叉搜索树(例如 TreeMap)都用于提供平均 O(log(N)) 访问。

For a given sort set, we can implement it as an independent class as follows:对于给定的排序集,我们可以将其实现为一个独立的类,如下所示:

class SortedSet {
  TreeMap<String, Integer>> keyToScore;
  TreeMap<Integer, Set<String>>> scoreToKey

  public SortedSet() {
    keyToScore= new TreeMap<>();
    scoreToKey= new TreeMap<>();
  }

  void addItem(String key, int score) {
    if (keyToScore.contains(key)) {
      // Remove old key and old score
    }
    // Add key and score to both maps 

  }

  List<String> getKeysInRange(int startScore, int endScore) {
     // traverse scoreToKey and retrieve all values
  }

  ....

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM