Java 从CopyOnWriteArrayList中删除元素
当我尝试使用迭代器从CopyOnWriteArrayList中删除元素时,出现了一个异常。 我注意到它被记录在案 不支持迭代器本身的元素更改操作(删除、设置和添加)。这些方法引发UnsupportedOperationException (来自) 现在,令人惊讶的是,我可以用foreach迭代它并使用remove()函数。但是我得到了一个著名的错误——当试图使用for循环从列表中删除一个项目时——您跳过了删除的元素旁边的元素。Java 从CopyOnWriteArrayList中删除元素,java,copy-on-write,Java,Copy On Write,当我尝试使用迭代器从CopyOnWriteArrayList中删除元素时,出现了一个异常。 我注意到它被记录在案 不支持迭代器本身的元素更改操作(删除、设置和添加)。这些方法引发UnsupportedOperationException (来自) 现在,令人惊讶的是,我可以用foreach迭代它并使用remove()函数。但是我得到了一个著名的错误——当试图使用for循环从列表中删除一个项目时——您跳过了删除的元素旁边的元素。 那有什么建议吗 编辑:我是个白痴。我错过了一个事实,这是一个副本上写
那有什么建议吗 编辑:我是个白痴。我错过了一个事实,这是一个副本上写的名单,所以每次删除意味着一个新的副本。因此,如果有多个删除,我下面的建议可能是次优的 对于迭代器不支持删除的任何其他列表,或者任何不使用迭代器的列表,都是如此。要避免此错误,需要考虑三种基本技术:
For(int i=0;i对集合进行迭代,选择要删除的所有元素并将其放入临时集合。迭代完成后,使用removeAll方法从原始集合中删除所有找到的元素
这对您有效吗?我的意思是,不确定删除逻辑是否比算法中的逻辑更复杂。通常,您会先在单独的列表中迭代要删除的元素集,然后在for each循环之外删除它们(无论如何,这是基于迭代器的伪装循环)类似于这样的内容:
int pos = 0;
while(pos < lst.size() ) {
Foo foo = lst.get(pos);
if( hasToBeRemoved(foo) ) {
lst.remove(pos);
// do not move position
} else {
pos++;
}
}
int pos=0;
而(位置
您可以使用而不是列表
private Queue<Something> queue = new ConcurrentLinkedQueue<Something>();
private Queue Queue=new ConcurrentLinkedQueue();
它是线程安全的,支持迭代器。remove()
。不过,请注意队列迭代器的线程安全行为(请查看javadoc)。因为这是一个CopyOnWriteArrayList
所以在使用forEach
进行迭代时删除元素是完全安全的。不需要花哨的算法
list.forEach(e -> {
if (shouldRemove(e))
list.remove(e);
});
编辑:如果您想通过引用而不是位置删除元素,那么这当然有效。最短、最有效的方法:
List<String> list = new CopyOnWriteArrayList<>();
list.removeIf(s -> s.length() < 1);
List List=new CopyOnWriteArrayList();
list.removeIf(s->s.length()<1);
它在内部创建一个长度相同的临时数组,并复制谓词返回true的所有元素
请记住,如果您使用此方法实际迭代元素以执行某些操作,则这些操作将无法并行执行,因为removeIf调用是原子的,并且将锁定其他线程的遍历。下面的可与CopyOnWriteArrayList配合使用
for(String key : list) {
if (<some condition>) {
list.remove(key);
}
}
for(字符串键:列表){
如果(){
列表。删除(键);
}
}
如果要删除所有元素,请使用just clear()。如果要保留元素,请将它们放在临时数组列表中,然后从中取回
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);
List tKeepThese=new ArrayList();
for(ListIterator tIter=copyonWriteArrayList;tIter.hasNext();)
{
tObject=滴度。下一步();
if(保留元素的条件)
t保留这些。添加(tObject);
}
copyonWritearraylist.clear();
copyonwritearraylist.addAll(保留这些内容);
我本来打算提出这个建议,但我决定不这样做,因为这样会更冗长,效率更低。如果它是正确的,那么冗长就不应该是一个问题。你为什么说它效率更低?相反,在这种情况下,你会强制对“写时复制”集合进行一次更改“。你不这么认为吗?在这种情况下,它更有效,因为每个remove()操作都会创建一个临时数组,removeAll()只会创建一次。Vladimir。你是对的。虽然我希望找到一种方法在列表上迭代。这会更好-只有一个循环。和一个内存实例。将用于(int I=arr.size()-1;I>=0;I-)对你有用吗?尽管如此,临时数组是一个更好的解决方案。第三个也是我的幸运赢家。但是我如何迭代回去。列表没有提供以前的。我现在认为我的答案是一个糟糕的答案(请参阅刚才编辑的答案)。但是如果你真的想这样做,我想你必须使用旧样式的for循环,并从list.length()开始-1.都德。不要对自己太苛刻。至少你是第一个:-)。列表有一个列表迭代器,它有一个hasPrevious()和previous()方法。您可以像这样设置列表迭代器的初始位置:myList.listIterator(myList.size())。然后向后迭代。所以,这是可能的,但正如你所建议的,在这种情况下,这不是一个好主意。这很好,但会让我们计算list.size很多。但这是可能的。计算大小发生在移除元素的过程中,移除是真正的性能损失。如前所述,更好的做法是收集所有要删除的元素到一个临时数组中,并立即删除它们。