Java 如何在迭代时从`ArrayList`中删除元素时避免“ConcurrentModificationException”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18448671/
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
How to avoid "ConcurrentModificationException" while removing elements from `ArrayList` while iterating it?
提问by Ernestas Gruodis
I'm trying to remove some elements from an ArrayList
while iterating it like this:
我正在尝试从 a 中删除一些元素,ArrayList
同时像这样迭代它:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
Of course, I get a ConcurrentModificationException
when trying to remove items from the list at the same time when iterating myArrayList
. Is there some simple solution to solve this problem?
当然,ConcurrentModificationException
当我尝试在迭代的同时从列表中删除项目时,我得到了myArrayList
。有没有一些简单的解决方案来解决这个问题?
采纳答案by arshajii
回答by Juned Ahsan
If you want to modify your List during traversal, then you need to use the Iterator
. And then you can use iterator.remove()
to remove the elements during traversal.
如果你想在遍历过程中修改你的 List,那么你需要使用Iterator
. 然后你可以iterator.remove()
在遍历过程中使用来删除元素。
回答by Eric Stein
You have to use the iterator's remove() method, which means no enhanced for loop:
您必须使用迭代器的 remove() 方法,这意味着没有增强的 for 循环:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
回答by Kevin DiTraglia
As an alternative to everyone else's answers I've always done something like this:
作为其他人答案的替代方案,我总是这样做:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
This will avoid you having to deal with the iterator directly, but requires another list. I've always preferred this route for whatever reason.
这将避免您必须直接处理迭代器,但需要另一个列表。无论出于何种原因,我一直更喜欢这条路线。
回答by Prabhakaran Ramaswamy
List myArrayList = Collections.synchronizedList(new ArrayList());
//add your elements
myArrayList.add();
myArrayList.add();
myArrayList.add();
synchronized(myArrayList) {
Iterator i = myArrayList.iterator();
while (i.hasNext()){
Object object = i.next();
}
}
回答by Prashant Bhate
While other suggested solutions work, If you really want the solution to be made thread safe you should replace ArrayList with CopyOnWriteArrayList
虽然其他建议的解决方案有效,但如果您真的希望解决方案成为线程安全的,则应将 ArrayList 替换为CopyOnWriteArrayList
//List<String> s = new ArrayList<>(); //Will throw exception
List<String> s = new CopyOnWriteArrayList<>();
s.add("B");
Iterator<String> it = s.iterator();
s.add("A");
//Below removes only "B" from List
while (it.hasNext()) {
s.remove(it.next());
}
System.out.println(s);
回答by CarlJohn
One alternative method is convert your List
to array
, iterate them and remove them directly from the List
based on your logic.
一种替代方法是将您的 转换List
为array
,迭代它们并List
根据您的逻辑直接从 中删除它们。
List<String> myList = new ArrayList<String>(); // You can use either list or set
myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");
Object[] obj = myList.toArray();
for(Object o:obj) {
if(condition)
myList.remove(o.toString());
}
回答by Gilo
回答by Mikhail Boyarsky
Java 8 user can do that: list.removeIf(...)
Java 8 用户可以这样做: list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
It will remove elements in the list, for which someCondition is satisfied
它将删除列表中满足 someCondition 的元素
回答by Dima Naychuk
No, no, NO!
不不不!
In single threated tasks you don't need to use Iterator, moreover, CopyOnWriteArrayList (due to performance hit).
在单个受威胁的任务中,您不需要使用迭代器,而且,CopyOnWriteArrayList(由于性能下降)。
Solution is much simpler: try to use canonical for loop instead of for-each loop.
解决方案要简单得多:尝试使用规范的 for 循环而不是 for-each 循环。
According to Java copyright owners (some years ago Sun, now Oracle) for-each loop guide, it uses iterator to walk through collection and just hides it to make code looks better. But, unfortunately as we can see, it produced more problems than profits, otherwise this topic would not arise.
根据 Java 版权所有者(几年前的 Sun,现在是 Oracle)for-each loop guide 的说法,它使用迭代器遍历集合并隐藏它以使代码看起来更好。但是,不幸的是,正如我们所看到的,它产生的问题比利润多,否则这个话题就不会出现。
For example, this code will lead to java.util.ConcurrentModificationException when entering next iteration on modified ArrayList:
例如,在修改后的 ArrayList 上进入下一次迭代时,此代码将导致 java.util.ConcurrentModificationException:
// process collection
for (SomeClass currElement: testList) {
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
}
}
But following code works just fine:
但是下面的代码工作得很好:
// process collection
for (int i = 0; i < testList.size(); i++) {
SomeClass currElement = testList.get(i);
SomeClass founDuplicate = findDuplicates(currElement);
if (founDuplicate != null) {
uniqueTestList.add(founDuplicate);
testList.remove(testList.indexOf(currElement));
i--; //to avoid skipping of shifted element
}
}
So, try to use indexing approach for iterating over collections and avoid for-each loop, as they are not equivalent! For-each loop uses some internal iterators, which check collection modification and throw ConcurrentModificationException exception. To confirm this, take a closer look at the printed stack trace when using first example that I've posted:
因此,尝试使用索引方法来迭代集合并避免 for-each 循环,因为它们是不等价的!For-each 循环使用一些内部迭代器,它们检查集合修改并抛出 ConcurrentModificationException 异常。要确认这一点,请在使用我发布的第一个示例时仔细查看打印的堆栈跟踪:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at TestFail.main(TestFail.java:43)
For multithreading use corresponding multitask approaches (like synchronized keyword).
对于多线程使用相应的多任务方法(如 synchronized 关键字)。