确保元素唯一性的队列?


68

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

有这种可能,还是我必须手工做?

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

欢迎任何输入。如果它不在java.util包中,那可能不是一个好主意?


1
TreeSet。这是一个排序的Set,并且Set表示“没有重复的元素”。
院长J,2010年

Answers:


53

怎么样LinkedHashSet?它的迭代器保留插入顺序,但是由于它是a Set,因此其元素是唯一的。

如其文档所述,

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

为了有效地从此“队列”的头部删除元素,请经历其迭代器:

Iterator<?> i = queue.iterator();
...
Object next = i.next();
i.remove();

6
问题在于它没有实现Queue,因此无法按FIFO顺序删除元素。
亚当斯基

2
@Adamski-按FIFO顺序删除元素很简单。查看我的更新。
erickson 2010年

1
足够容易地增强LinkedHashSet来添加推送和弹出。效率不高,但幼稚的pop可能是:Iterator <T> it = iterator(); T结果= it.next(); it.remove(); 返回结果;
Brandon DuRette

2
...尽管为每个删除操作创建一个迭代器似乎很丑陋。
亚当斯基

7
它还取决于在处理元素时是否要添加到队列的末尾。在处理从该队列中删除的元素的过程中添加到队列是明确定义的行为,但是使用Iterator,您会收到ConcurrentModificationException,因为内置的Java Collections假定它是线程问题,而不是滥用Collection及其Iterator的人好像两者结合在一起就是一个Queue实现。
西奥多·默多克

23

据我所知,这并不存在,但是将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.
}

尽管这可行,但它具有巨大的性能。我认为您既不需要集合,也不需要链表
Cshah 2010年

这也是tvanfosson提出的建议,与我已经拥有的建议非常接近。我只是对更标准的方式感到好奇。
Antoine Claval 2010年

3
@Cshah:你在说什么?tvanfosson的方法我的方法相同-他只是没有提供示例代码。同样,埃里克森使用LinkedHashSet的方法本质上是相同的,因为内部LinkedHashSet包含一个链表。使用“仅哈希集”将不会提供类似队列的行为。
Adamski

2
关于return trueadd。是不是有合同之间的冲突Collection#addQueue#add?该集合应该保证唯一性,因此应false根据Collectionjavadoc返回。同时,Queuejavadoc明确提到该方法是返回true或引发异常。docs.oracle.com/javase/7/docs/api/java/util/Queue.html#add(E)docs.oracle.com/javase/7/docs/api/java/util / ...不确定其中哪一个在这种情况下,应遵循两个合同。
toniedzwiedz 2015年

1
目的是实现队列处理的唯一性,queue#add肯定应该返回set#add的返回值,因为在调用该方法时您可能想知道该元素是否已经存在。此外,此类还应实现其余的Queue方法,例如element(),offer()poll(),peek()。除此之外,该课程肯定满足了需求
eric A

4

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


在这种情况下处理这种情况时,这似乎是一种方法: stackoverflow.com/questions/4447461/…–
Marc

4

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

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


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();
}
}

3
如果用ArrayDeque替换LinkedList,则比LinkedHashSet获得更好的轮询性能(x2),并且也应该击败实现。这里有一个博客帖子比较实现:psy-lob-saw.blogspot.com/2013/03/...
Nitsan Wakart

1
队列方法与set方法不同步,例如poll()也应从set中删除该元素,否则可能会发生这样的情况:您在代码中的某处询问!isEmpty(),然后在调用poll()时产生NPE。
UninformedUser

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>

1

这个问题问得好。没有现有的直接解决方案。我将挖掘一段时间后写的一些代码,尝试这样做,然后再编辑此答案。

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

编辑:对于需要并发的情况,我给出了以下答案: 并发设置队列


1

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

    Deque<Long> myQueue = new ArrayDeque<Long>() {
        @Override
        public boolean add(Long e) { return !this.contains(e) && super.add(e);}
    };
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.