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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 01:42:38  来源:igfitidea点击:

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

javalistarraylistiterator

提问by Ernestas Gruodis

I'm trying to remove some elements from an ArrayListwhile iterating it like this:

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

for (String str : myArrayList) {
    if (someCondition) {
        myArrayList.remove(str);
    }
}

Of course, I get a ConcurrentModificationExceptionwhen 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

Use an Iteratorand call remove():

使用Iterator并调用remove()

Iterator<String> iter = myArrayList.iterator();

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

    if (someCondition)
        iter.remove();
}

回答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 Listto array, iterate them and remove them directly from the Listbased on your logic.

一种替代方法是将您的 转换Listarray,迭代它们并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

You can use the iterator remove() function to remove the object from underlying collection object. But in this case you can remove the same object and not any other object from the list.

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

from here

这里

回答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 关键字)。