回答:
Java言語とランタイムには、既存の実装はありません。すべてのキューはAbstractQueueを拡張し、そのドキュメントには、完全なキューへの要素の追加は常に例外で終了することが明記されています。必要な機能を持たせるには、キューを独自のクラスにラップするのが最適です(そして非常に単純です)。
繰り返しになりますが、すべてのキューはAbstractQueueの子であるため、それを内部データ型として使用するだけで、実質的に短時間で柔軟な実装を実行できます。
更新:
以下に概説するように、2つのオープンな実装が利用可能です(この回答はかなり古いです、皆さん!)。詳細については、この回答を参照してください。
collection.deque
、を指定して使用できますmaxlen
。
実際、LinkedHashMapはまさにあなたが望んでいることをします。removeEldestEntry
メソッドをオーバーライドする必要があります。
最大10要素のキューの例:
queue = new LinkedHashMap<Integer, String>()
{
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
{
return this.size() > 10;
}
};
「removeEldestEntry」がtrueを返す場合、最も古いエントリがマップから削除されます。
この正しい答えを含む自分自身の重複した質問から、私は2つのことを学びました:
私はグアバを生産的に利用しEvictingQueue
、うまくいきました。
EvictingQueue
静的なファクトリメソッドcreate
を呼び出して最大サイズを指定するには、インスタンスを作成します。
EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ; // Set maximum size to 100.
CircularFifoQueue
リンクが無効です。
私はこの方法で固定サイズのキューを実装しました:
public class LimitedSizeQueue<K> extends ArrayList<K> {
private int maxSize;
public LimitedSizeQueue(int size){
this.maxSize = size;
}
public boolean add(K k){
boolean r = super.add(k);
if (size() > maxSize){
removeRange(0, size() - maxSize);
}
return r;
}
public K getYoungest() {
return get(size() - 1);
}
public K getOldest() {
return get(0);
}
}
removeRange(0, size() - maxSize)
これは、でQueue
ラップしたものLinkedList
です。ここで指定する固定サイズは2です。
public static Queue<String> pageQueue;
pageQueue = new LinkedList<String>(){
private static final long serialVersionUID = -6707803882461262867L;
public boolean add(String object) {
boolean result;
if(this.size() < 2)
result = super.add(object);
else
{
super.removeFirst();
result = super.add(object);
}
return result;
}
};
....
TMarket.pageQueue.add("ScreenOne");
....
TMarket.pageQueue.add("ScreenTwo");
.....
このクラスは、継承(代わりに他の回答)ではなく構成を使用して作業を行います(これにより、Essential JavaのJosh Blochでカバーされている)特定の副作用の可能性がなくなります。基になるLinkedListのトリミングは、add、addAll、offerメソッドで行われます。
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
public class LimitedQueue<T> implements Queue<T>, Iterable<T> {
private final int limit;
private final LinkedList<T> list = new LinkedList<T>();
public LimitedQueue(int limit) {
this.limit = limit;
}
private boolean trim() {
boolean changed = list.size() > limit;
while (list.size() > limit) {
list.remove();
}
return changed;
}
@Override
public boolean add(T o) {
boolean changed = list.add(o);
boolean trimmed = trim();
return changed || trimmed;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Iterator<T> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
@Override
public boolean remove(Object o) {
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
boolean changed = list.addAll(c);
boolean trimmed = trim();
return changed || trimmed;
}
@Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
}
@Override
public boolean offer(T e) {
boolean changed = list.offer(e);
boolean trimmed = trim();
return changed || trimmed;
}
@Override
public T remove() {
return list.remove();
}
@Override
public T poll() {
return list.poll();
}
@Override
public T element() {
return list.element();
}
@Override
public T peek() {
return list.peek();
}
}
public class CircularQueue<E> extends LinkedList<E> {
private int capacity = 10;
public CircularQueue(int capacity){
this.capacity = capacity;
}
@Override
public boolean add(E e) {
if(size() >= capacity)
removeFirst();
return super.add(e);
}
}
使用法とテスト結果:
public static void main(String[] args) {
CircularQueue<String> queue = new CircularQueue<>(3);
queue.add("a");
queue.add("b");
queue.add("c");
System.out.println(queue.toString()); //[a, b, c]
String first = queue.pollFirst(); //a
System.out.println(queue.toString()); //[b,c]
queue.add("d");
queue.add("e");
queue.add("f");
System.out.println(queue.toString()); //[d, e, f]
}
通常のリストのように聞こえますが、addメソッドには、リストが長すぎる場合にリストを切り捨てる追加のスニペットが含まれています。
それが単純すぎる場合は、おそらく問題の説明を編集する必要があります。
また、このSOの質問、またはArrayBlockingQueueを参照してください(ブロッキングに注意してください。これは、あなたの場合には望ましくない場合があります)。
あなたがこの質問をするようにあなたを導いたあなたのどんな要件があるのかは、はっきりしていません。固定サイズのデータ構造が必要な場合は、さまざまなキャッシュポリシーを確認することもできます。ただし、キューがあるので、ある種のルーター機能を探していると思います。その場合は、リングバッファー(最初と最後のインデックスを持つ配列)を使用します。要素が追加されるたびに、最後の要素のインデックスをインクリメントし、要素が削除されると、最初の要素のインデックスをインクリメントします。どちらの場合も、配列サイズを法として加算が実行され、必要に応じて、つまりキューがいっぱいまたは空になったときに、他のインデックスをインクリメントしてください。
また、それがルータータイプのアプリケーションの場合、ランダムアーリードロップ(RED)などのアルゴリズムを試してみると、キューがいっぱいになる前でも、要素がキューからランダムにドロップされます。場合によっては、REDは、ドロップする前にキューをいっぱいにする単純な方法よりも全体的なパフォーマンスが優れていることがわかっています。
最良の答えはこの他の質問からだと思います。
Apache commons collections 4には、あなたが探しているCircularFifoQueueがあります。javadocの引用:
CircularFifoQueueは、固定サイズの先入れ先出しキューであり、満杯の場合に最も古い要素を置き換えます。
OOPでアドバイスされているように、継承よりも構成を優先する必要があります
ここでそれを念頭に置いて私の解決策。
package com.choiceview;
import java.util.ArrayDeque;
class Ideone {
public static void main(String[] args) {
LimitedArrayDeque<Integer> q = new LimitedArrayDeque<>(3);
q.add(1);
q.add(2);
q.add(3);
System.out.println(q);
q.add(4);
// First entry ie 1 got pushed out
System.out.println(q);
}
}
class LimitedArrayDeque<T> {
private int maxSize;
private ArrayDeque<T> queue;
private LimitedArrayDeque() {
}
public LimitedArrayDeque(int maxSize) {
this.maxSize = maxSize;
queue = new ArrayDeque<T>(maxSize);
}
public void add(T t) {
if (queue.size() == maxSize) {
queue.removeFirst();
}
queue.add(t);
}
public boolean remove(T t) {
return queue.remove(t);
}
public boolean contains(T t) {
return queue.contains(t);
}
@Override
public String toString() {
return queue.toString();
}
}