Java:来自流源的前n个元素

时间:2012-03-06 09:56:53

标签: java algorithm sorting

假设您从“流”源读取数据项和相关分数(即无法随机访问或多次传递)。

在任何时候,只保留目前遇到的最低权重的内存元素的最佳方法是什么。我会对“Java”方式感兴趣,成语越短越好,而不是算法(“使用搜索树,插入新元素,如果超出大小则删除最大”)。

下面是我提出的解决方案,但是我发现它有点冗长,也有一些行为可能是意外的(具有不同分数的相同项目可能会保留多次,同时保留相同的项目添加相同的分数只有一次)。我也觉得应该有一些东西存在。

import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.Comparator;
import java.util.TreeSet;

/**
 * Stores the n smallest (by score) elements only.
 */
public class TopN<T extends Comparable<T>> {
  private TreeSet<Entry<T, Double>> elements;
  private int n;

  public TopN(int n) {
    this.n = n;
    this.elements = new TreeSet<Entry<T, Double>>(
        new Comparator<Entry<T, Double>>() {
          @Override
          public int compare(Entry<T, Double> o1, Entry<T, Double> o2) {
            if (o1.getValue() > o2.getValue()) return 1;
            if (o1.getValue() < o2.getValue()) return -1;
            return o1.getKey() == null ? 1 : o1.getKey().compareTo(o2.getKey());
          }
    });
  }

  /**
   * Adds the element if the score is lower than the n-th smallest score.
   */
  public void add(T element, double score) {
    Entry<T, Double> keyVal = new SimpleEntry<T, Double>(element,score);
    elements.add(keyVal);
    if (elements.size() > n) {
      elements.pollLast();
    }
  }

  /**
   * Returns the elements with n smallest scores.
   */
  public TreeSet<Entry<T, Double>> get() {
    return elements;
  }
}

有一个类似的问题,但它不包括流源/内存要求: Find top N elements in an Array

2 个答案:

答案 0 :(得分:5)

使用“堆”数据结构。 Java有一个内置的:PriorityQueue。只需将比较器定义为“最佳”,然后将流中的所有数据提供给优先级队列。

编辑:

要为此答案添加更多颜色,您可能需要执行以下操作:

  • 定义一个与你想要的方式相反的比较器(即赞成你要丢弃的项目) - 或者定义一个以正确方式工作的比较器,并用Collections.reverseOrder(...)
  • 包装它。
  • 迭代您的数据并将每个元素放入pqueue。
  • 对于每个插入,如果pqueue的大小> n,则使用poll()从堆中删除“top”元素 - 由于您的比较器,它实际上将是“最差”的元素

你剩下的是一个有n个元素的pqueue,其中“最不好”。

答案 1 :(得分:1)

你可以通过番石榴的Comparators课来获得理想的结果。请参阅下面的示例,该示例获得前5个数字。可以找到Api here

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collector;

import org.junit.Test;

import com.google.common.collect.Comparators;
import com.google.common.collect.Lists;

public class TestComparator {

    @Test
    public void testTopN() {
        final List<Integer> numbers = Lists.newArrayList(1, 3, 8, 2, 6, 4, 7, 5, 9, 0);
        final Collector<Integer, ?, List<Integer>> collector = Comparators.greatest(5,
                Comparator.<Integer>naturalOrder());
        final List<Integer> top = numbers.stream().collect(collector);
        System.out.println(top);
    }

}

输出:[9,8,7,6,5]