13

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

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

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

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

4

9 回答 9

22

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

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

于 2011-04-10T14:45:07.750 回答
7

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

与迭代器不支持删除的任何其他列表或您不使用迭代器的任何其他列表相同。为了避免这个错误,我想到了三种基本技术:

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

  2. 不知何故重复循环内部正在做的事情,而不是从字面上回到循环的顶部。有点小技巧 - 我会避免这种技术。

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

于 2011-04-10T14:44:57.187 回答
5

由于这是 aCopyOnWriteArrayList在使用 迭代时删除元素是完全安全的forEach。不需要花哨的算法。

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

编辑:如果你想通过引用而不是位置删除元素,那当然可以。

于 2016-11-24T09:37:15.053 回答
2

通常你会迭代首先收集 elemenet 以在单独的列表中删除,然后在 for each 循环之外删除它们(无论如何这是伪装的基于迭代器的循环)

于 2011-04-10T14:46:13.397 回答
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++;
  }
}
于 2011-04-10T14:48:59.287 回答
1

您可以使用队列而不是列表。

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

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

于 2014-06-12T13:42:19.697 回答
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);
于 2020-02-20T08:01:36.490 回答
0

最短和最有效的方法:

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

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

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

于 2018-01-11T10:42:35.927 回答
0

下面适用于 CopyOnWriteArrayList

for(String key : list) {
    if (<some condition>) {
        list.remove(key);
    }
}
于 2019-01-04T11:47:30.230 回答