Javaで最後のN個の要素を保持するサイズ制限のキュー


197

Javaライブラリに関する非常にシンプルで簡単な質問:Queue最大サイズが固定されたを実装する既成のクラスがあります。つまり、常に要素の追加を許可しますが、新しく追加された要素のスペースを収容するためにヘッド要素を静かに削除します。

もちろん、手動で実装するのは簡単です:

import java.util.LinkedList;

public class LimitedQueue<E> extends LinkedList<E> {
    private int limit;

    public LimitedQueue(int limit) {
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        super.add(o);
        while (size() > limit) { super.remove(); }
        return true;
    }
}

私の知る限り、Java stdlibsには標準の実装はありませんが、Apache Commonsなどに実装されている可能性がありますか?



9
@ケビン:あなたはそのようないじめです。
マークピーターズ

5
個人的には、これがこのライブラリーの唯一の使用である場合、別のライブラリーを紹介しません...
ニコラ・ブスケ

2
@Override public boolean add(PropagationTask t){boolean added = super.add(t); while(&& size()> limitを追加){super.remove(); }リターンが追加されました。}
ルノー、2013年

6
警告:問題のコードは、機能しているようですが、裏目に出る可能性があります。このサイズチェックを無視する要素をキューに追加できる追加のメソッド(addAll()など)があります。詳細については、Effective Java 2nd Edition-Item 16:Favor composition over inheritance
Diego

回答:


171

Apache commons collections 4には、探しているCircularFifoQueue <>があります。javadocの引用:

CircularFifoQueueは、固定サイズの先入れ先出しキューであり、満杯の場合に最も古い要素を置き換えます。

    import java.util.Queue;
    import org.apache.commons.collections4.queue.CircularFifoQueue;

    Queue<Integer> fifo = new CircularFifoQueue<Integer>(2);
    fifo.add(1);
    fifo.add(2);
    fifo.add(3);
    System.out.println(fifo);

    // Observe the result: 
    // [2, 3]

古いバージョンのApacheコモンズコレクション(3.x)を使用している場合は、 ジェネリックなしで基本的に同じもので CircularFifoBufferを。

更新:ジェネリックをサポートするcommonsコレクションバージョン4のリリース後に回答を更新しました。


1
これは良い候補ですが、
残念ながら

ありがとう!今のところ、これが最も実行可能な代替手段です:)
GreyCat

3
2013-10年頃にGoogle Guavaバージョン15 に追加されたリンクについては、この別の回答を参照してください。EvictingQueue
バジルブルク2014

フルキューへの追加により、要素がキューから削除されたときに呼び出されるコールバックはありますか?
ed22、2016年

「サーキュラーキュー」は、問題を満たす1つの実装にすぎません。しかし、この問題は、循環キューの主な違い、つまり各追加/削除で各バケットを解放/再割り当てする必要がないことから直接利益を得るものではありません。
simpleuser

90

グアバは、今持っているEvictingQueueキューに新しい要素を追加しようとしたときに自動的にキューの先頭から要素を追い出す非ブロックキューをし、それがいっぱいです。

import java.util.Queue;
import com.google.common.collect.EvictingQueue;

Queue<Integer> fifo = EvictingQueue.create(2); 
fifo.add(1); 
fifo.add(2); 
fifo.add(3); 
System.out.println(fifo); 

// Observe the result: 
// [2, 3]

これは、正式に発表されてから使用すると興味深いでしょう。
Asaf 2013年

ここで、ソースは次のとおりです。code.google.com/p/guava-libraries/source/browse/guava/src/com/... -それは、コピーやグアバの現在のリリースでコンパイルするのは簡単だろうように見える
トムCarchrae

1
更新:このクラスは、2013 年10月頃にバージョン15でGoogle Guavaとともに正式にリリースされました。
バジルブルク14

1
@MaciejMiklas質問はFIFOを要求するEvictingQueue、FIFOです。場合には任意の疑いがあり、このプログラムを試してみてください。 Queue<Integer> fifo = EvictingQueue.create(2); fifo.add(1); fifo.add(2); fifo.add(3); System.out.println(fifo); その結果を確認します[2, 3]
kostmo

2
これが正解です。ドキュメントからは少し不明確ですが、EvictingQueueはFIFOです。
MichaelBöckling2016年

11

@FractalizeRソリューションが好きです。しかし、私はさらにsuper.add(o)から値を保持して返します!

public class LimitedQueue<E> extends LinkedList<E> {

    private int limit;

    public LimitedQueue(int limit) {
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        boolean added = super.add(o);
        while (added && size() > limit) {
           super.remove();
        }
        return added;
    }
}

1
私が見る限り、FractalizeRは解決策を提供しておらず、質問を編集しただけです。質問内の「解決策」は解決策ではありません。質問は、独自のロールではなく、標準または準標準ライブラリのクラスを使用することに関するものだったためです。
GreyCat 2013年

3
このソリューションはスレッドセーフではないことを指摘しておく必要があります
Konrad Morawski

7
@KonradMorawski LinkedListクラス全体がいずれにしてもスレッドセーフではないため、このコンテキストではコメントは無意味です!
Renaud

@RenaudBlueスレッドの安全性は(しばしば見落とされている場合は)正当な懸念事項であるため、コメントは無意味だとは思いません。また、LinkedListスレッドセーフではないことを思い出しても無意味ではありません。この質問のコンテキストでは、OPの特定の要件により、アイテムの追加がアトミック操作として実行されることが特に重要になります。つまり、原子性を保証しないリスクは、通常のLinkedListの場合よりも大きくなります。
Konrad Morawski、

4
誰かがadd(int,E)代わりに電話するとすぐに壊れます。そしてaddAll、意図したとおりに機能するかどうかは、不特定の実装の詳細に依存します。そのため、継承よりも委任を優先する必要があります…
Holger

6

extendsではなく、compositionを使用します(つまり、Javaのextendsキーワードへの参照のように、extendsを意味し、これは継承です)。構成は実装を完全にシールドするため優れています。クラスのユーザーに影響を与えることなく実装を変更できます。

私はこのようなものを試すことをお勧めします(このウィンドウに直接入力しているので、購入者は構文エラーに注意してください):

public LimitedSizeQueue implements Queue
{
  private int maxSize;
  private LinkedList storageArea;

  public LimitedSizeQueue(final int maxSize)
  {
    this.maxSize = maxSize;
    storageArea = new LinkedList();
  }

  public boolean offer(ElementType element)
  {
    if (storageArea.size() < maxSize)
    {
      storageArea.addFirst(element);
    }
    else
    {
      ... remove last element;
      storageArea.addFirst(element);
    }
  }

  ... the rest of this class

(Asafの回答に基づく)より良いオプションは、Apache Collections CircularFifoBufferをジェネリッククラスでラップすることです。例えば:

public LimitedSizeQueue<ElementType> implements Queue<ElementType>
{
    private int maxSize;
    private CircularFifoBuffer storageArea;

    public LimitedSizeQueue(final int maxSize)
    {
        if (maxSize > 0)
        {
            this.maxSize = maxSize;
            storateArea = new CircularFifoBuffer(maxSize);
        }
        else
        {
            throw new IllegalArgumentException("blah blah blah");
        }
    }

    ... implement the Queue interface using the CircularFifoBuffer class
}

2
+1 あれば組成物は、(他の相続上の組成を好む」より)良い選択です...と非常に良い理由があるなぜ説明
kdgregory

1
ここでの構成は、私のタスクにとって不適切な選択です。つまり、オブジェクトの数の少なくとも2倍=>ガベージコレクションの頻度が2倍以上になることを意味します。Map <Long、LimitedSizeQueue <String >>のように、これらの制限されたサイズのキューを大量(数千万)に使用しています。
GreyCat

@GreyCat- LinkedListでは、実装方法をまだ見ていないと思います。リストのラッパーとして作成される追加のオブジェクトは、「数千万」のインスタンスがあっても、かなりマイナーです。
kdgregory

私は「インターフェースのサイズを小さくする」ことを目指していましたが、「実装のシールド」はほとんど同じです。どちらも、OPのアプローチに関するMark Peterの不満に答えます。
kdgregory

4

スペースが限られていることを私が知っている唯一のことは、BlockingQueueインターフェース(たとえば、ArrayBlockingQueueクラスによって実装されている)です。 )。

私の知る限り、あなたの簡単な実装は、そのような動作を実現する最も簡単な方法です。


私はすでにJava stdlibクラスを参照しましたが、残念BlockingQueueながら答えで はありません。Apache Commons、Eclipseのライブラリ、Spring、Googleの追加など、他の一般的なライブラリについて考えましたか?
GreyCat

3

あなたは使用することができますMinMaxPriorityQueueからGoogleのグアバを javadocツールから、:

min-maxプライオリティキューは、最大サイズで設定できます。その場合、キューのサイズがその値を超えるたびに、キューはそのコンパレーター(最大の要素は、追加されたばかりの要素である可能性があります)に従って自動的に削除します。これは、いっぱいになったときに新しい要素をブロックまたは拒否する従来の境界キューとは異なります。


3
プライオリティキューとは何か、およびそれがOPの例とどのように異なるかを理解していますか?
kdgregory

2
@マークピーターズ-何と言ったらいいかわからない。もちろん、優先度キューをfifoキューのように動作させることができます。のMapように動作させることもできListます。しかし、どちらのアイデアも、アルゴリズムとソフトウェア設計の完全な理解を示しています。
kdgregory

2
@マーク・ピーターズ- 何かをするための良い方法についてのすべての質問ではありませんか?
jtahlborn

3
@jtahlborn:明らかに(コードゴルフ)ではありませんが、たとえそうであったとしても、良いことは白黒の基準ではありません。あるプロジェクトでは、「最高」は「最も効率的」を意味し、別のプロジェクトでは「保守が最も簡単」を意味し、「別の場合」は「既存のライブラリを使用するコードの量が最も少ない」ことを意味します。私は、これは言ったことがないので、無関係であることのすべてだった良い答え。私は、それがあまり努力なしの解決策でありえると言いました。MinMaxPriorityQueueOPが望むものにaを変換することは、a を変更することよりも簡単ですLinkedList(OPのコードが近づくことすらありません)。
マークピーターズ

3
たぶんあなたたちは私の言葉の「実際にはほぼ間違いなく十分だ」という言葉を検討しているのかもしれません。このソリューションがOPの問題や一般的にはほぼ確実に十分であることを意味していませんでした。私はlong自分の提案の中でカーソルタイプとして降順の選択を参照していました。理論的には2 ^ 64を超えるオブジェクトをこのキューに追加でき、その時点で解決策が壊れたとしても、実際には十分に広いと述べました。
Mark Peters


-2
    public class ArrayLimitedQueue<E> extends ArrayDeque<E> {

    private int limit;

    public ArrayLimitedQueue(int limit) {
        super(limit + 1);
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        boolean added = super.add(o);
        while (added && size() > limit) {
            super.remove();
        }
        return added;
    }

    @Override
    public void addLast(E e) {
        super.addLast(e);
        while (size() > limit) {
            super.removeLast();
        }
    }

    @Override
    public boolean offerLast(E e) {
        boolean added = super.offerLast(e);
        while (added && size() > limit) {
            super.pollLast();
        }
        return added;
    }
}

3
問題は、人気のあるコレクションクラスライブラリのクラスに関するものであり、独自のものではありません-最小限の自作「解決策」はすでに問題となっています。
GreyCat 2013

2
グーグルは問題ではありません別のクエリでもこのページを検索=)
user590444 '17

1
この答えは低品質のレビューキューで判明しました。おそらくコードの説明を提供していないためです。このコードが質問に回答する場合は、回答にコードを説明するテキストを追加することを検討してください。このようにして、より多くの賛成票を獲得し、質問者が何か新しいことを学ぶのを助ける可能性がはるかに高くなります。
lmo
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.