web-dev-qa-db-ja.com

Java固定サイズのPriorityQueue

結果として得られるアルゴリズムの組み合わせを多数計算しています。この組み合わせを並べ替えるには、二重の値で評価し、PriorityQueueに保存します。現在、そのキューには約200kのアイテムがあり、かなりメモリ集約的です。実際には、リスト内のすべてのアイテムの中で最高の1000または100と言うだけで十分です。そのため、Javaで固定サイズの優先度キューを作成する方法があるかどうかを考え始めました。私はこのように振る舞う必要があります:アイテムはすでに保存されているものよりも優れていますか?はいの場合は、適切な位置に挿入し、評価が最も低い要素を捨てます。

誰かがアイデアを持っていますか?本当にありがとうございました!

マルコ

37
Marco
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;
        }
31
getekha

MinMaxPriorityQueue、Googleグアバ

実際、キューを維持するためのクラスがあり、コレクションの最大サイズを超えるアイテムを追加するときに、アイテムを比較して削除するアイテムを見つけ、それによって部屋を作成します: MinMaxPriorityQueue は、バージョン8以降 Google Guava にあります。

EvictingQueue

ちなみに、オブジェクトの値を比較せずに最も古い要素を削除したいだけの場合、Google Guava 15は EvictingQueue クラス。

12
Basil Bourque

Apache Luceneには固定サイズの優先度キューがあります: http://lucene.Apache.org/Java/2_4_1/api/org/Apache/lucene/util/PriorityQueue.html

私のテストに基づいて、それは優れたパフォーマンスを持っています。

5

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);   
}
2
Victor Sorokin

キューの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億を超える値の場合、2つのオブジェクト(IterableIterator)と1つのintが必要です。

もちろん、コードをIterableを使用しないように簡単に変更することもできます。これは、シーケンスを表現するエレガントな方法であるため使用しただけです(また、あまりにも多くのことを行ってきましたPythonおよびC#☺)。

2
gustafc

アイテムを追加するたびに上位1000を保持するのは自然なことのようですが、PriorityQueueはそれを優雅に達成するために何も提供していません。おそらく、PriorityQueueを使用する代わりに、メソッドで次のようなことを行うことができます。

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

より良いアプローチは、プログラムの実行時にキューに入れられるものをより厳密にモデレートし、削除して追加することです。キューに追加する前に一部のアイテムを除外する余地があるようです。いわば、ホイールを再発明するよりも簡単です。

0
Gordon