有效地删除添加到ConcurrentQueue的元素

时间:2015-01-29 23:39:08

标签: java collections concurrency concurrenthashmap

原则上,从ConcurrentLinkedQueue或类似实现中删除元素很容易。例如,该类的Iterator支持有效的O(1)删除当前元素:

public void remove() {
    Node<E> l = lastRet;
    if (l == null) throw new IllegalStateException();
    // rely on a future traversal to relink.
    l.item = null;
    lastRet = null;
}

我想用add()向队列中添加一个元素,然后从队列中删除该确切元素。我能看到的唯一选择是:

  1. 保存对该对象的引用并使用该对象调用ConcurrentLinkedQueue.remove(Object o) - 但这会在最坏的情况下强制遍历整个队列(平均有一半随机添加和删除模式)。

    这还有一个问题,即它不一定会删除我插入的相同的对象。它会删除相等的对象,如果队列中的多个对象相等,则该对象可能是不同的。

  2. 使用ConcurrentLinkedDeque代替addLast()我的元素,然后立即抓住descendingIterator()并迭代,直到找到我的元素(通常是第一个元素,但可能因为我正在有效地游走并发潮流。)

    这是一个笨拙且可能非常慢的补充,这迫使我使用Deque类,在这种情况下,对于许多操作来说复杂得多且速度慢一些(查看该类的Iterator.remove()!)

    此外,如果可以插入相同的(即==标识),此解决方案仍然具有细微的故障模式,因为我可能会发现其他人插入了该对象,但在通常情况下可以忽略该对象可能的。

  3. 这两个解决方案看起来都很尴尬,但删除这些结构中的任意元素似乎是一个核心操作。我错过了什么?

    在我看来,这是其他并发列表和出列的一般问题,甚至是非LinkedList等非并发结构。

    C ++以insert等方法的形式提供它。

2 个答案:

答案 0 :(得分:0)

不,在Java API中没有任何方法可以做到这一点;它并不是真正的核心操作。

对于它的价值而言,首先如何处理它会有一些重大的技术难题。例如,考虑ArrayList。如果向ArrayList添加元素,则会为您提供另一个参考对象,告诉您该元素的位置......

  • 您要为每个add操作添加分配
  • 每个对象必须跟踪对其“指针”对象的一个​​(或多个!)引用,这至少会使数据结构的内存消耗增加一倍
  • 每次在ArrayList中插入元素时,都必须为位置偏移的每个其他元素更新指针对象

简而言之,虽然这对于基于链表的数据结构来说可能是一个合理的操作,但实际上并没有任何好的方法可以将它拟合到更通用的API中,所以它几乎被省略了。

答案 1 :(得分:0)

如果您特别需要此功能(例如,您将拥有大量集合),那么您可能需要实现自己的集合,该集合返回对添加条目的引用,并且能够在O(1)中删除。

class MyLinkedList<V> {
    public class Entry {
        private Entry next;
        private Entry prev;
        private V value;
    }

    public Entry add(V value) {
        ...
    }

    public void remove(Entry entry) {
        ...
    }
}

换句话说,您不是按值删除,而是通过引用集合条目:

MyLinkedList<Integer> intList;
MyLinkedList.Entry entry = intList.add(15);
intList.remove(entry);

这显然需要付出相当多的工作量。