72

我正在寻找 java.util.Queue 的实现或谷歌集合中的东西,它们的行为类似于队列,但还要确保队列的每个元素都是唯一的。(所有进一步的插入都没有效果)

这是可能的,还是我必须手动完成?

现在我使用的是一个队列,一个 LinkedList 实现,我在插入之前检查了唯一性。(我使用侧地图来执行此操作,在队列之前/之后从侧地图中添加/删除元素)。我不太喜欢它。

欢迎任何意见。如果它不在 java.util 包中,那么也许这是一个坏主意?

4

8 回答 8

56

怎么样LinkedHashSet?它的迭代器保留了插入顺序,但是因为它是 a Set,所以它的元素是唯一的。

正如其文档所述,

请注意,如果将元素重新插入集合中,则插入顺序不受影响

为了有效地从这个“队列”的头部删除元素,请通过它的迭代器:

Iterator<?> i = queue.iterator();
...
Object next = i.next();
i.remove();
于 2010-02-23T15:05:44.860 回答
25

据我所知,这并不存在,但将 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.
}
于 2010-02-23T15:08:31.307 回答
5

我很想维护一个包含一个键的HashSet,该键与它并排唯一地标识队列中的项目。然后在添加之前检查 HashSet 以查看该项目是否在队列中。当您从队列中删除一个项目时,只需从 HashSet 中删除该键即可。

于 2010-02-23T15:05:11.550 回答
5

只是为了完成亚当斯基的回答:

/**
 * 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();
}
}
于 2013-03-05T10:24:03.903 回答
4

检查唯一性当然有成本(空间或时间)。似乎从 PriorityQueue 之类的东西中工作可能会很有趣,它将维护一个按元素的 Comparator 排序的堆。您可以利用它来更有效地(O(log n))检查存在而无需维护侧图。

如果您确实想用唯一性检查器包装队列,我强烈建议您使用 Google Collections ForwardingQueue来构建这样的东西。

于 2010-02-23T15:12:28.590 回答
2

我回答有点晚了,但我最终使用 ArrayDeque 解决了一个类似的问题,并覆盖了我需要的 add 方法。

    Deque<Long> myQueue = new ArrayDeque<Long>() {
        @Override
        public boolean add(Long e) { return !this.contains(e) && super.add(e);}
    };
于 2019-06-22T11:02:49.160 回答
2

不幸的是它不存在。因为我需要这样一个队列,所以我开发了一个由一组支持的阻塞队列,灵感来自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>
于 2016-11-09T20:33:35.003 回答
0

这个问题问得好。没有现有的直接解决方案。我会挖掘一些我不久前写的试图做到这一点的代码,然后回来编辑这个答案。

编辑:我回来了。确实,如果您不需要并发,最好分别维护 Queue 和 Set 。对于我正在做的事情,并发性是一个目标,但考虑到这个约束,我能想出的最佳解决方案是有问题的;基本上,由于它使用了 ConcurrentHashMap,因此您从队列中删除“头”元素的次数越多(与队列有关的基本操作),哈希表就会随着时间的推移变得越不平衡。我仍然可以与您分享此代码,但我怀疑您是否真的想要它。

编辑:对于需要并发的情况,我给出了这个答案: 并发集队列

于 2010-02-23T15:54:29.327 回答