当我尝试使用迭代器从CopyOnWriteArrayList中删除元素时,我收到异常。 我注意到它已记录在案
不支持对迭代器本身进行元素更改操作(删除,设置和添加)。这些方法抛出UnsupportedOperationException。
(来自http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html)
现在,令人惊讶的是我可以使用foreach迭代它并使用remove()函数。但后来我得到了着名的错误 - 当尝试使用for循环从列表中删除项目时 - 跳过被删除元素旁边的元素。 那有什么建议吗?
答案 0 :(得分:21)
迭代集合,选择要删除的所有元素并将其放入临时集合中。完成迭代后,使用removeAll方法从原始集合中删除所有找到的元素。
这会对你有用吗?我的意思是,不确定删除逻辑是否比算法中的更复杂。
答案 1 :(得分:7)
编辑:我是个白痴。我错过了这是一个写入时复制列表的事实,因此每次删除都意味着一个新的副本。因此,如果有多个删除,我的建议可能不是最理想的。
与迭代器不支持remove的任何其他列表相同,或者任何你没有使用迭代器的列表。为避免此错误,有三种基本技术可供考虑:
删除某些内容后减少索引(注意在下一次迭代之前不要对索引执行任何操作)。为此,您显然必须使用for(int i=0; i <
...样式的for循环,以便您可以操作索引。
以某种方式重复循环内部正在做的事情,而不是直接回到循环的顶部。一点点黑客 - 我会避免这种技术。
反向迭代列表(从头到尾,而不是从头到尾)。我更喜欢这种方法,因为它是最简单的。
答案 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);
}
}