Java.util.Queueの実装、またはキューのように動作するGoogleコレクション内の何かを探していますが、キューの各要素が一意であることも確認しています。 (それ以上挿入しても効果はありません)
それは可能ですか、それとも手でやらなければなりませんか?
今のところ、LinkedList実装でQueueを使用しており、挿入前に一意性を確認しています。 (私はこれを行うためにサイドマップを使用し、キューの前後にサイドマップから要素を追加/削除します)。あまり好きではありません。
任意の入力を歓迎します。 Java.utilパッケージにない場合、それは悪い考えでしょうか?
LinkedHashSet
?その反復子は挿入順序を保持しますが、Set
であるため、その要素は一意です。
そのドキュメントが言うように、
要素がre-insertedである場合、挿入順序はnot影響を受けることに注意してくださいセットに。
この「キュー」の先頭から要素を効率的に削除するには、イテレータを通過します。
Iterator<?> i = queue.iterator();
...
Object next = i.next();
i.remove();
これは私が知る限り存在しませんが、LinkedList
とSet
を組み合わせて使用することで、かなり簡単に実装できます。
/**
* Thread unsafe implementation of UniqueQueue.
*/
public class UniqueQueue<T> implements Queue<T> {
private final Queue<T> queue = new LinkedList<T>();
private final Set<T> set = new HashSet<T>();
public boolean add(T t) {
// Only add element to queue if the set does not contain the specified element.
if (set.add(t)) {
queue.add(t);
}
return true; // Must always return true as per API def.
}
public T remove() throws NoSuchElementException {
T ret = queue.remove();
set.remove(ret);
return ret;
}
// TODO: Implement other Queue methods.
}
キュー内の項目を並べて一意に識別するキーを含む HashSet を維持したいと思うでしょう。次に、HashSetをチェックして、アイテムを追加する前にキューにあるかどうかを確認します。キューからアイテムを削除するときは、HashSetからもキーを削除するだけです。
コースの一意性を確認するにはコストがかかります(空間または時間のいずれかで)。要素のコンパレータによってソートされたヒープを維持するPriorityQueueのようなものから作業することは興味深いかもしれません。これを活用して、サイドマップを維持することなく、より効率的に(O(log n))存在を確認できる場合があります。
キューを一意性チェッカーでラップしたい場合は、Google Collections ForwardingQueue を使用してそのようなものを作成することを強くお勧めします。
Adamskiの答えを完成させるために:
/**
* A queue that keeps each element only once.
* If you try to add an element that already exists - nothing will happen.
*
* @author Adamski http://stackoverflow.com/a/2319156/827927
* @NotThreadSafe
*/
public class UniqueQueue<T> implements Queue<T> {
private final Queue<T> queue = new LinkedList<T>();
private final Set<T> set = new HashSet<T>();
@Override public boolean add(T t) {
// Only add element to queue if the set does not contain the specified element.
if (set.add(t))
queue.add(t);
return true; // Must always return true as per API def.
}
@Override public boolean addAll(Collection<? extends T> arg0) {
boolean ret = false;
for (T t: arg0)
if (set.add(t)) {
queue.add(t);
ret = true;
}
return ret;
}
@Override public T remove() throws NoSuchElementException {
T ret = queue.remove();
set.remove(ret);
return ret;
}
@Override public boolean remove(Object arg0) {
boolean ret = queue.remove(arg0);
set.remove(arg0);
return ret;
}
@Override public boolean removeAll(Collection<?> arg0) {
boolean ret = queue.removeAll(arg0);
set.removeAll(arg0);
return ret;
}
@Override public void clear() {
set.clear();
queue.clear();
}
@Override public boolean contains(Object arg0) {
return set.contains(arg0);
}
@Override public boolean containsAll(Collection<?> arg0) {
return set.containsAll(arg0);
}
@Override public boolean isEmpty() {
return set.isEmpty();
}
@Override public Iterator<T> iterator() {
return queue.iterator();
}
@Override public boolean retainAll(Collection<?> arg0) {
throw new UnsupportedOperationException();
}
@Override public int size() {
return queue.size();
}
@Override public Object[] toArray() {
return queue.toArray();
}
@Override public <T> T[] toArray(T[] arg0) {
return queue.toArray(arg0);
}
@Override public T element() {
return queue.element();
}
@Override public boolean offer(T e) {
return queue.offer(e);
}
@Override public T peek() {
return queue.peek();
}
@Override public T poll() {
return queue.poll();
}
}
答えるのが少し遅れましたが、ArrayDequeを使用して同様の問題を解決し、必要なaddメソッドをオーバーライドしました。
Deque<Long> myQueue = new ArrayDeque<Long>() {
@Override
public boolean add(Long e) { return !this.contains(e) && super.add(e);}
};
これは非常に良い質問です。既存の簡単なソリューションはありません。しばらく前に書いたコードを掘り下げて、これを実行しようとし、戻ってこの回答を編集します。
EDIT:戻ってきました。確かに、並行性が必要ない場合は、キューとセットを別々に維持する方が良いでしょう。私がやっていたことについては、並行性が目標でしたが、その制約を考えると思いつく最高の解決策は問題でした。基本的に、ConcurrentHashMapを使用しているため、キューから「ヘッド」要素を削除するほど(キューを処理するための基本的なこと)、ハッシュテーブルは時間の経過とともに不均衡になります。私はまだこのコードをあなたと共有できますが、本当にあなたがそれを望んでいるとは思いません。
EDIT:並行性が必要な場合、私はこの答えを与えました: Concurrent Set Queue
残念ながら存在しません。このようなキューが必要だったので、Java.util.concurrent.LinkedBlockingQueueに触発されたセットに裏打ちされたブロッキングキューを開発しました。
ここで見つけることができます:
https://github.com/bvanalderweireldt/concurrent-unique-queue
例:
final BlockingQueue<Integer> queue = new ConcurrentSetBlockingQueue<>(1);
queue.offer(new Integer(1)); //True
queue.offer(new Integer(1)); //False
Mavenで使用できます:
<dependency>
<groupId>com.hybhub</groupId>
<artifactId>concurrent-util</artifactId>
<version>0.1</version>
</dependency>