使用Java流的MinMaxPriorityQueue

使用Java流的MinMaxPriorityQueue,java,sorting,java-8,guava,java-stream,Java,Sorting,Java 8,Guava,Java Stream,我正在Java中寻找一种内存高效的方法,从一个巨大的集合中查找前n个元素。例如,我有一个单词、一个distance()方法和一组“all”单词。 我已经实现了一个类对,它实现了compareTo(),这样就可以根据它们的值对这些对进行排序 使用streams,我的天真解决方案如下所示: double distance(String word1, String word2){ ... } Collection<String> words = ...; String word =

我正在Java中寻找一种内存高效的方法,从一个巨大的集合中查找前n个元素。例如,我有一个单词、一个distance()方法和一组“all”单词。 我已经实现了一个类对,它实现了compareTo(),这样就可以根据它们的值对这些对进行排序

使用streams,我的天真解决方案如下所示:

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);
双距离(字符串字1、字符串字2){
...
}
集合词=。。。;
字符串字=“…”;
words.stream()
.map(w->新对(w,距离(单词,w)))
.已排序()
.限制(n);
据我所知,这将处理每个元素并将其中间存储在word中,以便在应用limit()之前对其进行排序。但是,如果有一个存储n个元素的集合,则内存效率更高。每当添加新元素时,它都会删除最小的元素(根据可比较对象的自然顺序),因此永远不会超过n(或n+1)

这正是番石榴的作用。因此,我目前对上述问题的最佳解决方案是:

Queue<Pair<String, Double>> neighbours = MinMaxPriorityQueue.maximumSize(n).create();
words.stream()
  .forEach(w -> neighbours.add(new Pair<String, Double>(w, distance(word, w)));
Queue neights=MinMaxPriorityQueue.maximumSize(n.create();
words.stream()
.forEach(w->neights.add)(新对(w,距离(单词,w)));
在将队列转换为流或列表后,仍需对前n个元素进行排序,但这不是问题,因为n相对较小


我的问题是:有没有一种方法可以使用streams实现同样的功能?

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

MinMaxPriorityQueue<Pair<String, Double>> topN = words.stream()
    .map(w -> new Pair<String, Double>(w, distance(word, w)))
    .collect(toCollection(
            () -> MinMaxPriorityQueue.maximumSize(n).create()
    ));
MinMaxPriorityQueue topN=words.stream()
.map(w->新对(w,距离(单词,w)))
收集(
()->MinMaxPriorityQueue.maximumSize(n).create()
));
这比
.forEach
解决方案要好,因为它易于并行化,并且更习惯于java8


请注意,
()->MinMaxPriorityQueue.maximumSize(n).create()
应该可以替换为
MinMaxPriorityQueue.maximumSize(n)::create
,但由于某些原因,它在某些条件下无法编译(请参阅下面的注释).

非常感谢@Misha,这似乎是我一直在寻找的解决方案。但是,由于您上次在代码中进行编辑,它不再编译:
类型MinMaxPriorityQueue.Builder没有定义适用于此处的create()
。不过,它在以前的形式中还可以:
toCollection()->MinMaxPriorityQueue.maximumSize(n).create())
。您更改的原因是什么?@Carsten odd。我刚刚尝试过,使用jdk 1.8.0编译后效果良好_25@Carsten更少的括号使它更清晰(符合我的口味)。你用什么版本的jdk编译这个?我完全同意你的观点,我无法找出问题的原因,但它仍然存在。我使用的是Oracle jdk 1.8.040。这就是为什么我不愿意接受你的答案。@Carsten非常奇怪。我明天早上会戳它。与此同时,我将它改回lambda表达式形式。这不是在
MinMaxPriorityQueue
的所有要点:
MinMaxPriorityQueue
的唯一要点是当您实际需要一个双端优先级队列来访问最大和最低元素时,这里似乎不是这样的情况。
maximumSize
方面不是数据结构的要点,这将是最重要的效率极低。Guava的
排序。greatestOf
函数针对这个确切的用例进行了严格优化,需要O(n)时间而不是O(n log n)。(Guava的未来版本将有一个
排序版本。greatestOf
专门设计用于匹配Java 8收集器API。)谢谢你的指点!我看不到使用Ordering.greatestOf()的方法没有先在集合中中间存储所有元素,有吗?或者这只可能在您提到的未来版本中实现?它不需要是集合,它可以是
Iterable
Iterator
。如果您有一个流,您可以执行
Ordering.natural().greatestOf(stream.Iterator(),k)
@LouisWasserman,你在未来版本的番石榴中说过。你是否也可以链接任何票证以便我们跟踪进度?我想我正在寻找类似
.stream().collect(myOrdering.greatestOfCollector(5))