我正在尝试在迭代ArrayList
时从中删除一些元素,代码如下:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
当我尝试在同时迭代myArrayList
并从列表中删除项时,会出现ConcurrentModificationException
。有没有简单的解决方法来解决这个问题?
我正在尝试在迭代ArrayList
时从中删除一些元素,代码如下:
for (String str : myArrayList) {
if (someCondition) {
myArrayList.remove(str);
}
}
当我尝试在同时迭代myArrayList
并从列表中删除项时,会出现ConcurrentModificationException
。有没有简单的解决方法来解决这个问题?
String str = iter.next();
上也遇到了相同的异常! Java 中的集合糟糕透了! - Al-Mothafar作为其他人答案的替代方案,我通常会做类似于这样的事情:
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
if (someCondition) {
toRemove.add(str);
}
}
myArrayList.removeAll(toRemove);
这样做可以避免你直接处理迭代器,但需要另一个列表。出于某些原因,我总是更喜欢这种方法。
Java 8用户可以这样做:list.removeIf(...)
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.removeIf(e -> (someCondition));
它将删除列表中满足一些条件的元素。removeWhile
,那就太好了。 - Konstantin MilyutinremoveWhile
和removeIf
会删除所有符合条件的元素。 - António Almeida你需要使用迭代器的remove()方法,这意味着不能使用增强型for循环:
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
iterator.next();
if (someCondition) {
iterator.remove();
}
}
不,不,不!
在单线程任务中,您不需要使用Iterator,而且CopyOnWriteArrayList(由于性能问题)也不需要。
解决方案要简单得多:尝试使用传统的for循环而不是for-each循环。
根据Java版权所有者(几年前是Sun,现在是Oracle)的for-each循环指南,它使用迭代器遍历集合,仅将其隐藏以使代码看起来更好。但是,不幸的是,正如我们所看到的那样,它产生了比收益更多的问题,否则就不会出现这个问题。
例如,当进入修改后的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));
}
}
但是以下代码可以正常工作:
// 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
}
}
因此,尝试使用索引方法来迭代集合并避免使用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)
对于多线程,请使用相应的多任务方法(如 synchronized 关键字)。
get(i)
方法要 高效得多。 - Angadfor (int i = testList.size() - 1; i >= 0; i--) { ... }
此外,您可以用testList.remove(i);
来替换testList.remove(testList.indexOf(currElement));
。 - Martin Rust虽然其他建议的解决方案也可以工作,但如果你真的希望解决方案是线程安全的,你应该用CopyOnWriteArrayList代替ArrayList。
//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);
如果您希望在遍历期间修改列表,则需要使用Iterator
。然后,您可以使用iterator.remove()
来删除遍历过程中的元素。
一种替代方法是将您的 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());
}
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();
}
}