【问题标题】:MinMaxPriorityQueue using Java streams使用 Java 流的 MinMaxPriorityQueue
【发布时间】:2015-03-31 07:46:39
【问题描述】:

我正在寻找一种在 Java 中节省内存的方法来从庞大的集合中查找前 n 个元素。例如,我有一个单词、一个 distance() 方法和一个“所有”单词的集合。 我已经实现了一个实现 compareTo() 的类 Pair,以便对按它们的值排序。

使用流,我的幼稚解决方案如下所示:

double distance(String word1, String word2){
  ...
}

Collection<String> words = ...;
String word = "...";

words.stream()
  .map(w -> new Pair<String, Double>(w, distance(word, w)))
  .sorted()
  .limit(n);

据我了解,这将处理并以单词形式存储每个元素,以便在应用 limit() 之前对其进行排序。但是,拥有一个存储 n 个元素的集合更节省内存,并且每当添加一个新元素时,它都会删除最小的元素(根据可比较对象的自然顺序),因此永远不会增长大于 n(或 n+1 )。

这正是 Guava MinMaxPriorityQueue 所做的。因此,我目前对上述问题的最佳解决方案是:

Queue<Pair<String, Double>> neighbours = MinMaxPriorityQueue.maximumSize(n).create();
words.stream()
  .forEach(w -> neighbours.add(new Pair<String, Double>(w, distance(word, w)));

将队列转换为流或列表后,前n个元素的排序仍有待完成,但这不是问题,因为n相对较小。

我的问题是:有没有办法使用流来做同样的事情?

【问题讨论】:

  • 这根本不是MinMaxPriorityQueue 的重点:MinMaxPriorityQueue 的唯一重点是当您实际上需要一个双端优先级队列来访问最大和最低元素时,确实如此这里似乎不是这样。 maximumSize 方面不是数据结构的重点,这将严重低效。 Guava 的 Ordering.greatestOf 函数针对这个确切的用例进行了严格优化,并且需要 O(n) 时间而不是 O(n log n)。
  • (Guava 的未来版本将有一个 Ordering.greatestOf 版本,专门设计用于匹配 Java 8 Collector API。)
  • 感谢指点!我看不到使用 Ordering.greatestOf() 而不先将所有元素中间存储在集合中的方法,有吗?或者这只会在你提到的未来版本中才有可能?
  • 不需要是集合,可以是IterableIterator。如果你有一个流,你可以做Ordering.natural().greatestOf(stream.iterator(), k)
  • @LouisWasserman,你在番石榴的未来版本中说过。您是否也可以链接任何票证以便我们跟踪进度?我想我正在寻找类似.stream().collect(myOrdering.greatestOfCollector(5)) 的东西。

标签: java sorting java-8 guava java-stream


【解决方案1】:

基于堆的结构当然比对整个巨大列表进行排序更有效。幸运的是,流库非常乐意让您在必要时使用专门的集合:

MinMaxPriorityQueue<Pair<String, Double>> topN = words.stream()
    .map(w -> new Pair<String, Double>(w, distance(word, w)))
    .collect(toCollection(
            () -> MinMaxPriorityQueue.maximumSize(n).create()
    ));

这比 .forEach 解决方案更好,因为它易于并行化并且更符合 java8 的习惯。

请注意,() -&gt; MinMaxPriorityQueue.maximumSize(n).create() 应该可以替换为 MinMaxPriorityQueue.maximumSize(n)::create,但由于某种原因,它在某些条件下无法编译(参见下面的 cmets)。

【讨论】:

  • 非常感谢@Misha,这似乎是我正在寻找的解决方案。但是,自从您上次编辑代码以来,它不再编译:The type MinMaxPriorityQueue.Builder&lt;Comparable&gt; does not define create() that is applicable here。不过,它以前的形状很好:toCollection(() -&gt; MinMaxPriorityQueue.maximumSize(n).create())。你改变的原因是什么?
  • @Carsten 奇怪。我刚试了一下,它用 jdk 1.8.0_25 编译得很好
  • @Carsten 更少的括号使它更清晰(根据我的口味)。你用什么版本的jdk编译这个?
  • 我完全同意你的观点,我无法找出问题的原因,但它仍然存在。我正在使用 Oracle JDK 1.8.0_40。这就是为什么我犹豫接受你的回答。
  • @Carsten 很奇怪。我会在早上戳它。同时,我把它改回了 lambda 表达式形式。
猜你喜欢
  • 1970-01-01
  • 2010-12-30
  • 2018-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-14
相关资源
最近更新 更多