java 从 CopyOnWriteArrayList 中删除元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5612470/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
remove elements from CopyOnWriteArrayList
提问by Bick
I am getting an exception when I try to remove elements from CopyOnWriteArrayList using an iterator. I have noticed that it is documented
当我尝试使用迭代器从 CopyOnWriteArrayList 中删除元素时出现异常。我注意到它被记录在案
Element-changing operations on iterators themselves (remove, set, and add) are not supported. These methods throw UnsupportedOperationException.
不支持迭代器本身的元素更改操作(删除、设置和添加)。这些方法抛出 UnsupportedOperationException。
(from http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html)
(来自http://download.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html)
Now, surprisingly i can iterate it with foreach and use the remove() function . But then I get the famous bug - when trying to remove an item from a list using a for loop - you skip the element next to the removed element. any suggestions then?
现在,令人惊讶的是我可以用 foreach 迭代它并使用 remove() 函数。但是后来我得到了一个著名的错误——当尝试使用 for 循环从列表中删除一个项目时——你跳过了被删除元素旁边的元素。有什么建议吗?
回答by Edwin Dalorzo
Iterate over the collection choosing all the elements you want to delete and putting those in a temporary collection. After you finish iteration remove all found elements from the original collection using method removeAll.
遍历集合,选择要删除的所有元素并将它们放入临时集合中。完成迭代后,使用 removeAll 方法从原始集合中删除所有找到的元素。
Would that work out for you? I mean, not sure if deletion logic is more complicated than that in your algorithm.
这对你有用吗?我的意思是,不确定删除逻辑是否比您的算法更复杂。
回答by Robin Green
EDIT:I'm an idiot. I missed the fact that this is a copy-on-write list so every removal means a new copy. So my suggestions below are likely to be suboptimal if there's more than one removal.
编辑:我是个白痴。我错过了一个事实,即这是一个写时复制列表,因此每次删除都意味着一个新副本。因此,如果删除不止一个,我下面的建议可能不是最理想的。
Same as for any other list whose iterator doesn't support remove, or anything where you're not using an iterator. There are three basic techniques that come to mind to avoid this bug:
与迭代器不支持删除的任何其他列表或您不使用迭代器的任何其他列表相同。为了避免这个错误,我想到了三种基本技术:
Decrement the index after removing something (being careful not to do anything with the index until the next iteration). For this you'll obviously have to use a
for(int i=0; i <
... style of for loop, so that you can manipulate the index.Somehow repeat what the inside of the loop is doing, without literally going back to the top of the loop. Bit of a hack - I would avoid this technique.
Iterate over the list in reverse (from end to start, instead of from start to end). I prefer this approach as it's the simplest.
删除某些内容后减少索引(注意不要对索引进行任何操作,直到下一次迭代)。为此,您显然必须使用
for(int i=0; i <
... 风格的 for 循环,以便您可以操作索引。以某种方式重复循环内部正在执行的操作,而无需返回循环顶部。有点黑客 - 我会避免这种技术。
反向迭代列表(从头到尾,而不是从头到尾)。我更喜欢这种方法,因为它是最简单的。
回答by Hooman Valibeigi
Since this is a CopyOnWriteArrayList
it is totally safe to remove elements while iterating with forEach
. No need for fancy algorithms.
由于这是 ,CopyOnWriteArrayList
因此在使用 迭代时删除元素是完全安全的forEach
。不需要花哨的算法。
list.forEach(e -> {
if (shouldRemove(e))
list.remove(e);
});
EDIT:Well of course that works if you want to delete elements by reference, not by position.
编辑:当然,如果您想按引用而不是按位置删除元素,那当然可行。
回答by Tomasz Stanczak
Ususlly you would iterate first gathering elemenet to be deleted in a separate list then delete them outside the for each loop (which is disguised iterator based loop anyway)
通常,您会先迭代收集要在单独列表中删除的元素,然后在 for each 循环之外删除它们(无论如何,这都是基于迭代器的伪装循环)
回答by Vladimir Dyuzhev
Something like this:
像这样的东西:
int pos = 0;
while(pos < lst.size() ) {
Foo foo = lst.get(pos);
if( hasToBeRemoved(foo) ) {
lst.remove(pos);
// do not move position
} else {
pos++;
}
}
回答by Nacho Coloma
You could use Queueinstead of List.
您可以使用队列而不是列表。
private Queue<Something> queue = new ConcurrentLinkedQueue<Something>();
It's thread safe and supports iterator.remove()
. Be aware of the thread-safe behavior of Queue iterators, though (check the javadoc).
它是线程安全的并且支持iterator.remove()
. 不过,请注意队列迭代器的线程安全行为(查看 javadoc)。
回答by benez
the shortest and most efficient way:
最短和最有效的方法:
List<String> list = new CopyOnWriteArrayList<>();
list.removeIf(s -> s.length() < 1);
internally it creates an temporary array with the same length and copies all elements where the predicate returns true.
在内部,它创建一个具有相同长度的临时数组,并复制谓词返回 true 的所有元素。
keep in mind that if you use this method to actually iterate over the elements to perform some action, these actions cannot be performed in paralell anymore since the removeIf-call is atomic and will lock the traversal for other threads
请记住,如果您使用此方法实际迭代元素以执行某些操作,则这些操作不能再并行执行,因为 removeIf 调用是原子的,并且会锁定其他线程的遍历
回答by omar
Below works fine with CopyOnWriteArrayList
下面使用 CopyOnWriteArrayList 工作正常
for(String key : list) {
if (<some condition>) {
list.remove(key);
}
}
回答by The incredible Jan
If you want to delete all use just clear(). If you want to keep elements put them in a temporary ArrayList and get them back from there.
如果你想删除所有使用 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);