使用Java流的MinMaxPriorityQueue

时间:2015-03-31 07:46:39

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

我正在寻找一种内存高效的Java方法,以便从庞大的集合中找到前n个元素。例如,我有一个单词,一个distance()方法,以及一个" all"话。 我已经实现了一个实现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相对较小。

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

1 个答案:

答案 0 :(得分:2)

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

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。

请注意,() -> MinMaxPriorityQueue.maximumSize(n).create()应该可以替换为MinMaxPriorityQueue.maximumSize(n)::create,但由于某种原因,在某些情况下无法编译(请参阅下面的评论)。