我正在寻找java.util.Queue或Google集合中某个行为类似于Queue的东西的实现,但也要确保队列中的每个元素都是唯一的。 (所有进一步插入都没有效果)
这是可能的,还是我必须手工完成?
现在我正在使用带有LinkedList实现的Queue,并在插入之前检查唯一性。 (我使用侧面Map来执行此操作,在队列之前/之后添加/删除侧面图中的元素)。我不太喜欢它。
欢迎任何输入。如果它不在java.util包中,那么也许这是一个坏主意?
答案 0 :(得分:44)
LinkedHashSet
怎么样?它的迭代器保留了插入顺序,但由于它是Set
,因此它的元素是唯一的。
正如其文档所述,
请注意,如果元素重新插入到该集合中,则插入顺序不会受到影响。
为了有效地从这个“队列”的头部删除元素,请通过它的迭代器:
Iterator<?> i = queue.iterator();
...
Object next = i.next();
i.remove();
答案 1 :(得分:20)
据我所知,这不存在,但使用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.
}
答案 2 :(得分:4)
我很想保留一个HashSet,其中包含一个唯一标识队列中项目的密钥。然后在添加之前检查HashSet以查看该项是否在队列中。从队列中删除项目时,只需从HashSet中删除该项。
答案 3 :(得分:4)
检查课程的唯一性会产生成本(空间或时间)。看起来像从PriorityQueue这样的东西工作可能会很有趣,它将维护一个按比较元素排序的堆。您可能能够更有效地利用它(O(log n))检查是否存在而无需维护侧面图。
如果您确实想要使用唯一性检查程序包装Queue,我强烈建议您使用Google Collections ForwardingQueue来构建此类内容。
答案 4 :(得分:4)
完成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();
}
}
答案 5 :(得分:1)
这是一个非常好的问题。没有现成的直接解决方案。我会在一段时间内编写一些试图执行此操作的代码,然后返回并编辑此答案。
编辑:我回来了。确实,如果您不需要并发性,最好分开维护队列和集合。对于我正在做的事情,并发性是一个目标,但鉴于约束是有问题的,我可以提出最好的解决方案;基本上,因为它使用了ConcurrentHashMap,所以从队列中删除“head”元素的次数越多(与队列基本相关),哈希表随着时间的推移会越不平衡。我仍然可以与你分享这些代码,但我怀疑你真的想要它。
编辑:对于需要并发的情况,我给出了这个答案: Concurrent Set Queue
答案 6 :(得分:1)
不幸的是它并不存在。由于我需要这样一个队列,所以我开发了一个由一个集合支持的阻塞队列,受到 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>
答案 7 :(得分:0)
我来晚了一点,但最终我使用ArrayDeque解决了一个类似的问题,并覆盖了我需要的add方法。
.