从CopyOnWriteArrayList中删除元素

时间:2011-04-10 14:39:12

标签: java copy-on-write

当我尝试使用迭代器从CopyOnWriteArrayList中删除元素时,我收到异常。 我注意到它已记录在案

  

不支持对迭代器本身进行元素更改操作(删除,设置和添加)。这些方法抛出UnsupportedOperationException。

(来自http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html

现在,令人惊讶的是我可以使用foreach迭代它并使用remove()函数。但后来我得到了着名的错误 - 当尝试使用for循环从列表中删除项目时 - 跳过被删除元素旁边的元素。 那有什么建议吗?

9 个答案:

答案 0 :(得分:21)

迭代集合,选择要删除的所有元素并将其放入临时集合中。完成迭代后,使用removeAll方法从原始集合中删除所有找到的元素。

这会对你有用吗?我的意思是,不确定删除逻辑是否比算法中的更复杂。

答案 1 :(得分:7)

编辑:我是个白痴。我错过了这是一个写入时复制列表的事实,因此每次删除都意味着一个新的副本。因此,如果有多个删除,我的建议可能不是最理想的。

与迭代器不支持remove的任何其他列表相同,或者任何你没有使用迭代器的列表。为避免此错误,有三种基本技术可供考虑:

  1. 删除某些内容后减少索引(注意在下一次迭代之前不要对索引执行任何操作)。为此,您显然必须使用for(int i=0; i < ...样式的for循环,以便您可以操作索引。

  2. 以某种方式重复循环内部正在做的事情,而不是直接回到循环的顶部。一点点黑客 - 我会避免这种技术。

  3. 反向迭代列表(从头到尾,而不是从头到尾)。我更喜欢这种方法,因为它是最简单的。

答案 2 :(得分:4)

由于这是CopyOnWriteArrayList,因此在使用forEach进行迭代时删除元素是完全安全的。不需要花哨的算法。

list.forEach(e -> {
    if (shouldRemove(e))
        list.remove(e);
});

编辑:当然,如果你想通过引用而不是按位置删除元素,那么它是有效的。

答案 3 :(得分:2)

通常你会在一个单独的列表中迭代第一个收集元素,然后在每个循环之外删除它们(无论如何都是伪装的基于迭代器的循环)

答案 4 :(得分:1)

这样的事情:

int pos = 0;
while(pos < lst.size() ) {
  Foo foo = lst.get(pos);
  if( hasToBeRemoved(foo) ) {
    lst.remove(pos);
    // do not move position
  } else {
    pos++;
  }
}

答案 5 :(得分:1)

您可以使用Queue代替List。

private Queue<Something> queue = new ConcurrentLinkedQueue<Something>();

它是线程安全的并且支持iterator.remove()。但请注意Queue迭代器的线程安全行为(检查javadoc)。

答案 6 :(得分:1)

如果要删除所有内容,请使用clear()。如果要保留元素,请将其放入临时ArrayList中,然后从那里取回。

List<Object> tKeepThese= new ArrayList<>();
for(ListIterator<Object> tIter = theCopyOnWriteArrayList; tIter.hasNext();)
{
    tObject = tIter.next();
    if(condition to keep element)
        tKeepThese.add(tObject);
}
theCopyOnWriteArrayList.clear();
theCopyOnWriteArrayList.addAll(tKeepThese);

答案 7 :(得分:0)

最短,最有效的方式:

List<String> list = new CopyOnWriteArrayList<>();
list.removeIf(s -> s.length() < 1);

在内部创建一个具有相同长度的临时数组,并复制谓词返回true的所有元素。

请记住,如果您使用此方法实际迭代元素以执行某些操作,则由于removeIf调用是原子的并且将锁定其他线程的遍历,因此无法再以并行方式执行这些操作

答案 8 :(得分:0)

以下与CopyOnWriteArrayList配合使用

for(String key : list) {
    if (<some condition>) {
        list.remove(key);
    }
}