一个确保元素唯一性的队列?

时间:2010-02-23 15:01:25

标签: java collections queue guava unique-constraint

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

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

现在我正在使用带有LinkedList实现的Queue,并在插入之前检查唯一性。 (我使用侧面Map来执行此操作,在队列之前/之后添加/删除侧面图中的元素)。我不太喜欢它。

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

8 个答案:

答案 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方法。

.