具有固定大小的Java PriorityQueue

时间:2009-12-04 10:54:10

标签: java list size priority-queue

我正在计算一个algortihm的大量可能的结果组合。要对这些组合进行排序,我使用双倍值对它们进行评级,并将它们存储在PriorityQueue中。目前,该队列中有大约20万个项目,这几乎是内存集成。实际上,我只需要说出列表中所有项目中最好的1000或100。 所以我开始问自己是否有办法在Java中拥有一个固定大小的优先级队列。我应该这样做: 该项目是否比已经存储的更好?如果是,请将其插入相应的位置并抛出最小等级的元素。

有没有人有想法?非常感谢!

7 个答案:

答案 0 :(得分:28)

que.add(d);
if (que.size() > YOUR_LIMIT)
     que.poll();

还是我错过了解你的问题?

编辑:忘记提及为了使这个工作你可能必须反转你的comparTo函数,因为它会丢弃每个周期具有最高优先级的那个。 (如果a是“更好”b比较(a,b)应该返回一个正数。

保持最大数字的例子使用类似的东西:

public int compare(Double first, Double second) {
            // keep the biggest values
            return first > second ? 1 : -1;
        }

答案 1 :(得分:11)

MinMaxPriorityQueue,Google Guava

确实存在一个用于维护队列的类,当添加超过集合最大大小的项目时,比较这些项目以查找要删除的项目,从而创建空间:MinMaxPriorityQueue在{ {3}}从版本8开始。

EvictingQueue

顺便说一句,如果您只想删除最旧的元素而不对对象的值进行任何比较,那么Google Guava 15就获得了Google Guava类。

答案 2 :(得分:5)

Apache Lucene中有一个固定大小的优先级队列:http://lucene.apache.org/java/2_4_1/api/org/apache/lucene/util/PriorityQueue.html

根据我的测试,它具有出色的性能。

答案 3 :(得分:2)

每次添加项目时保持前1000名似乎很自然,但PriorityQueue没有提供任何可以优雅地实现这一目标的内容。也许你可以,而不是使用PriorityQueue,在方法中做这样的事情:

List<Double> list = new ArrayList<Double>();
...
list.add(newOutput);
Collections.sort(list);
list = list.subList(0, 1000);

答案 4 :(得分:2)

使用SortedSet:

SortedSet<Item> items = new TreeSet<Item>(new Comparator<Item>(...));
...
void addItem(Item newItem) {
    if (items.size() > 100) {
         Item lowest = items.first();
         if (newItem.greaterThan(lowest)) {
             items.remove(lowest);
         }
    }

    items.add(newItem);   
}

答案 5 :(得分:1)

如果队列中的最小元素小于(在您的情况下,评分低于当前元素),则只需poll()队列。

static <V extends Comparable<? super V>> 
PriorityQueue<V> nbest(int n, Iterable<V> valueGenerator) {
    PriorityQueue<V> values = new PriorityQueue<V>();
    for (V value : valueGenerator) {
        if (values.size() == n && value.compareTo(values.peek()) > 0)
            values.poll(); // remove least element, current is better
        if (values.size() < n) // we removed one or haven't filled up, so add
            values.add(value);
    }
    return values;
}

这假设您有某种实现Comparable的组合类,用于比较其评级的组合。

修改:为了澄清,我的示例中的Iterable不需要预先填充。例如,这里有Iterable<Integer>,它会为您提供int可以表示的所有自然数:

Iterable<Integer> naturals = new Iterable<Integer>() {
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            int current = 0;
            @Override
            public boolean hasNext() {
                return current >= 0;
            }
            @Override
            public Integer next() {
                return current++;
            }
            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
};

内存消耗非常适中,如您所见 - 对于超过20亿个值,您需要两个对象(IterableIterator)加一个int

你当然可以很容易地调整我的代码,因此它不使用Iterable - 我只是使用它,因为它是一种表达序列的优雅方式(同样,我一直在做太多Python和C#☺)。

答案 6 :(得分:0)

更好的方法是更紧密​​地调整队列中的内容,在程序运行时删除并附加到队列中。听起来在将一些项目添加到队列之前会有一些空间来排除它们。它可以说比重新发明轮子更简单。