我正在寻找 java.util.Queue 的实现或谷歌集合中的东西,它们的行为类似于队列,但还要确保队列的每个元素都是唯一的。(所有进一步的插入都没有效果)
这是可能的,还是我必须手动完成?
现在我使用的是一个队列,一个 LinkedList 实现,我在插入之前检查了唯一性。(我使用侧地图来执行此操作,在队列之前/之后从侧地图中添加/删除元素)。我不太喜欢它。
欢迎任何意见。如果它不在 java.util 包中,那么也许这是一个坏主意?
我正在寻找 java.util.Queue 的实现或谷歌集合中的东西,它们的行为类似于队列,但还要确保队列的每个元素都是唯一的。(所有进一步的插入都没有效果)
这是可能的,还是我必须手动完成?
现在我使用的是一个队列,一个 LinkedList 实现,我在插入之前检查了唯一性。(我使用侧地图来执行此操作,在队列之前/之后从侧地图中添加/删除元素)。我不太喜欢它。
欢迎任何意见。如果它不在 java.util 包中,那么也许这是一个坏主意?
怎么样LinkedHashSet
?它的迭代器保留了插入顺序,但是因为它是 a Set
,所以它的元素是唯一的。
正如其文档所述,
请注意,如果将元素重新插入集合中,则插入顺序不受影响。
为了有效地从这个“队列”的头部删除元素,请通过它的迭代器:
Iterator<?> i = queue.iterator();
...
Object next = i.next();
i.remove();
据我所知,这并不存在,但将 aLinkedList
与 a 结合使用会相当简单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 中删除该键即可。
只是为了完成亚当斯基的回答:
/**
* 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();
}
}
检查唯一性当然有成本(空间或时间)。似乎从 PriorityQueue 之类的东西中工作可能会很有趣,它将维护一个按元素的 Comparator 排序的堆。您可以利用它来更有效地(O(log n))检查存在而无需维护侧图。
如果您确实想用唯一性检查器包装队列,我强烈建议您使用 Google Collections ForwardingQueue来构建这样的东西。
我回答有点晚了,但我最终使用 ArrayDeque 解决了一个类似的问题,并覆盖了我需要的 add 方法。
Deque<Long> myQueue = new ArrayDeque<Long>() {
@Override
public boolean add(Long e) { return !this.contains(e) && super.add(e);}
};
不幸的是它不存在。因为我需要这样一个队列,所以我开发了一个由一组支持的阻塞队列,灵感来自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>
这个问题问得好。没有现有的直接解决方案。我会挖掘一些我不久前写的试图做到这一点的代码,然后回来编辑这个答案。
编辑:我回来了。确实,如果您不需要并发,最好分别维护 Queue 和 Set 。对于我正在做的事情,并发性是一个目标,但考虑到这个约束,我能想出的最佳解决方案是有问题的;基本上,由于它使用了 ConcurrentHashMap,因此您从队列中删除“头”元素的次数越多(与队列有关的基本操作),哈希表就会随着时间的推移变得越不平衡。我仍然可以与您分享此代码,但我怀疑您是否真的想要它。
编辑:对于需要并发的情况,我给出了这个答案: 并发集队列