我有这段小代码,它给了我并发修改异常。我无法理解为什么我会继续得到它,即使我没有看到任何同时进行的修改。
import java.util.*;
public class SomeClass {
public static void main(String[] args) {
List<String> s = new ArrayList<>();
ListIterator<String> it = s.listIterator();
for (String a : args)
s.add(a);
if (it.hasNext())
String item = it.next();
System.out.println(s);
}
}
答案 0 :(得分:51)
要避免使用ConcurrentModificationException
,您应该编写如下代码:
import java.util.*;
public class SomeClass {
public static void main(String[] args) {
List<String> s = new ArrayList<String>();
for(String a : args)
s.add(a);
ListIterator<String> it = s.listIterator();
if(it.hasNext()) {
String item = it.next();
}
System.out.println(s);
}
}
java.util.ListIterator
允许您在迭代期间修改列表,但不能在创建和使用它之间修改列表。
答案 1 :(得分:44)
我无法理解为什么我会继续得到它,即使我没有看到任何同时进行的修改。
在创建迭代器和开始使用迭代器之间,您向要迭代的列表添加了参数。这是一个并发修改。
ListIterator<String> it = s.listIterator();
for (String a : args)
s.add(a); // concurrent modification here
if (it.hasNext())
String item = it.next(); // exception thrown here
在完成向列表中添加元素后创建迭代器:
for (String a : args)
s.add(a);
ListIterator<String> it = s.listIterator();
if (it.hasNext())
String item = it.next();
答案 2 :(得分:11)
来自JavaDoc:对于ConcurrentModificatoinException:“一个线程修改一个集合而另一个线程正在迭代它时通常不允许”。
它只是意味着如果你仍然有一个打开的迭代器,则不允许修改列表,因为迭代器循环会中断。尝试移动ListIterator<String> it = s.listIterator();
直到for循环。
答案 3 :(得分:8)
修改基础列表后,不允许继续迭代迭代器。在这里你先创建迭代器,然后再向s
添加几个项目,然后在添加之后继续对其进行hasNext()
和next()
,导致ConcurrentModificationException
< / p>
答案 4 :(得分:5)
如果上述解决方案无法正常运行。您可以使用旧的for循环来迭代List,同时添加新项。 请参阅以下示例:
import java.util.*;
public class SomeClass {
public static void main(String[] args) {
ArrayList<AClass> aList = new ArrayList<AClass>(); // we will iterate this
// this will cause ConcurrentModificationException.
// Since we are iterating the list, at the same time modifying it.
/*for(AClass a: aList){
aList.add(someMethod(a));
}*/
// old fashion for-loop will help
int limit = aList.size();
for(int i=0; ctr<limit; ++i){
AClass a = aList.get(i);
aList.add(someMethod(a));
}
}
}
答案 5 :(得分:3)
ConcurrentModificationException 可能出现在单线程环境和多线程环境中。 主要的问题是,所有通用迭代器(如ArrayList中使用的迭代器)都是 FailFast迭代器,当我们尝试修改一个列表时,如果一个迭代器已经迭代它,它就会失败。 解决方案 - &gt;如果需求需要这样的场景而不是使用ArrayList,请使用CopyOnWriteArrayList。
对于完整的演示,可以使用下面提到的代码。 我们只需要将实现从CopyOnWriteArrayList更改为ArrayList。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author narif
*
*/
public class TestApp {
/**
* @param args
*/
public static void main(String[] args) {
List<String> testList = new ArrayList<>();
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add("abc");
testList.add(6, "abcAtindex6");
int size = testList.size();
System.out.println("The Current List (ArrayList) is: " + testList);
System.out.println("The size of the List (ArrayList) is: " + size);
/* Comment the below lines to get the ConcurrentModificationException */
testList = new CopyOnWriteArrayList<>(testList);
for (String value : testList) {
System.out.println("The Value from ForEach Loop is: " + value);
/*
* Concurrent modification is happening here
* One iterator is iterating over the list while we are trying to add new values to
* the list so the results of the iteration are undefined under these circumstances.
* So teh fail fast iterators will fail and will throw the ConcurrentModificationException.
*/
testList.add("valueFromForLoop");
testList.add("anotherValueFromForEachLoop");
}
Iterator<String> it = testList.iterator();
while (it.hasNext()) {
String abc = it.next();
System.out.println(abc);
testList.add("Value from Iterator1");
testList.add("Value from Iterator2");
testList.add("Value from Iterator3");
testList.add("Value from Iterator4");
}
System.out.println("Did the modificationa and all after conevrting the ArrayList to CopyOnWriteArrayList.");
System.out.println("Calling the method to get the new List..");
testList = new CopyOnWriteArrayList<>(getTheList(testList));
for (String value : testList) {
System.out.println("The value returned from method is : " + value);
}
}
private static List<String> getTheList(List<String> pList) {
List<String> list = new CopyOnWriteArrayList<>(pList);
int i = 0;
for (String lValue : list) {
System.out.println("The list Passed is " + list);
i++;
list.add("localVaueFromMethod" + i);
list.removeAll(pList);
}
return list;
}
}
如需更多信息,请点击此链接,这可能会有所帮助ConcurrentModificationException Java Docs
答案 6 :(得分:1)
这不起作用:
LinkedList<String> linkedList = new LinkedList<String>();
ListIterator listIterator = linkedList.listIterator();
linkedList.add("aa");
linkedList.add("bb");
这有效:
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("aa");
linkedList.add("bb");
ListIterator listIterator = linkedList.listIterator();
答案 7 :(得分:1)
理解这一点让我们看看HashMap实现的来源:
public class HashMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable{
包含HashIterator,如下所示:
private abstract class HashIterator {
...
int expectedModCount = modCount;
...
HashMapEntry<K, V> nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
....
}
每次创建迭代器时:
为了避免这种情况你可以:
这将允许您同时迭代并添加或删除元素而不会出现异常
并发映射/列表迭代器是一个“弱一致”的迭代器 永远不会抛出ConcurrentModificationException,并保证 在构造迭代器时存在的遍历元素, 并且可能(但不保证)反映任何修改 在施工之后。
答案 8 :(得分:1)
查看oracle documentation页面。
public class ConcurrentModificationException
extends RuntimeException
当不允许进行此类修改时,检测到并发修改对象的方法可能抛出此异常
请注意,此异常并不总是表示某个对象已被另一个线程同时修改。如果单个线程发出违反对象合同的一系列方法调用,则该对象可能会抛出此异常。 例如,如果一个线程在使用失败快速迭代器迭代集合时直接修改集合,则迭代器将抛出此异常。
在您的情况下,您在创建迭代器后修改了集合,因此您遇到了异常。
如果您按照 Stephen C 回答更改了代码,则不会出现此错误。