关于java:如何在迭代时从“ArrayList”中删除元素时避免“ConcurrentModificationException”?

How to avoid “ConcurrentModificationException” while removing elements from `ArrayList` while iterating it?

本问题已经有最佳答案,请猛点这里访问。

我试图从ArrayList中删除一些元素,同时像这样迭代它:

1
2
3
4
5
for (String str : myArrayList) {
    if (someCondition) {
        myArrayList.remove(str);
    }
}

当然,当我在迭代myArrayList的同时尝试从列表中删除项目时,会得到一个ConcurrentModificationException。有什么简单的解决办法来解决这个问题吗?


使用Iterator并调用remove()

1
2
3
4
5
6
7
8
Iterator<String> iter = myArrayList.iterator();

while (iter.hasNext()) {
    String str = iter.next();

    if (someCondition)
        iter.remove();
}


作为对其他人答案的替代,我总是这样做:

1
2
3
4
5
6
7
List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
    if (someCondition) {
        toRemove.add(str);
    }
}
myArrayList.removeAll(toRemove);

这将避免直接处理迭代器,但需要另一个列表。不管什么原因,我总是喜欢这条路线。


Java 8用户可以做到这一点:EDCOX1 OR 7

1
2
    List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
    list.removeIf(e -> (someCondition));

它将删除列表中满足某些条件的元素


您必须使用迭代器的remove()方法,这意味着没有增强的for循环:

1
2
3
4
5
6
for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
    iterator.next();
    if (someCondition) {
        iterator.remove();
    }
}


不,不,不!

在单线程任务中,您不需要使用迭代器,而且,CopyOnWriteArrayList(由于性能下降)。

解决方案要简单得多:尝试使用规范的for循环,而不是针对每个循环。

根据Java版权所有者(几年前Sun,现在Oracle)的每个循环指南,它使用迭代器来遍历集合,只是隐藏它,使代码看起来更好。但不幸的是,正如我们所看到的,它产生的问题比利润更多,否则这个话题就不会出现。

例如,在修改后的arraylist上输入下一个迭代时,此代码将导致java.util.ConcurrentModificationException:

1
2
3
4
5
6
7
8
9
        // process collection
        for (SomeClass currElement: testList) {

            SomeClass founDuplicate = findDuplicates(currElement);
            if (founDuplicate != null) {
                uniqueTestList.add(founDuplicate);
                testList.remove(testList.indexOf(currElement));
            }
        }

但是下面的代码工作得很好:

1
2
3
4
5
6
7
8
9
10
11
    // 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
        }
    }

因此,尝试使用索引方法迭代集合,并避免对每个循环使用,因为它们不是等价的!对于每个循环,使用一些内部迭代器,检查集合修改并引发ConcurrentModificationException异常。要确认这一点,请在使用我发布的第一个示例时仔细查看打印的堆栈跟踪:

1
2
3
4
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关键字)。


当其他建议的解决方案起作用时,如果您真的希望使解决方案具有线程安全性,则应将arraylist替换为copyonwritearraylist。

1
2
3
4
5
6
7
8
9
10
11
    //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);


另一种方法是将您的List转换为array,迭代它们,并根据您的逻辑直接从List中删除它们。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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());
}


1
2
3
4
5
6
7
8
9
10
11
12
13
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();
     }
 }


如果要在遍历期间修改列表,则需要使用Iterator。然后可以使用iterator.remove()在遍历期间删除元素。


可以使用迭代器remove()函数从基础集合对象中移除该对象。但在这种情况下,您可以从列表中删除同一个对象,而不是任何其他对象。

从这里