387

我试图从这样的ArrayList迭代中删除一些元素:

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

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

4

10 回答 10

619

使用Iterator并调用remove()

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

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

    if (someCondition)
        iter.remove();
}
于 2013-08-26T16:29:00.610 回答
215

作为其他所有人答案的替代方案,我一直在做这样的事情:

List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
    if (someCondition) {
        toRemove.add(str);
    }
}
myArrayList.removeAll(toRemove);

这将避免您必须直接处理迭代器,但需要另一个列表。无论出于何种原因,我一直更喜欢这条路线。

于 2013-08-26T16:35:07.663 回答
100

Java 8 用户可以这样做:list.removeIf(...)

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

它将删除列表中满足 someCondition 的元素

于 2014-04-22T17:00:46.473 回答
70

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

for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
    iterator.next();
    if (someCondition) {
        iterator.remove();
    }
}
于 2013-08-26T16:29:07.257 回答
41

不不不!

此外,在单个受威胁任务中,您不需要使用 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)

对于多线程使用相应的多任务方法(如同步关键字)。

于 2014-11-14T14:41:16.383 回答
9

虽然其他建议的解决方案有效,但如果您真的希望解决方案成为线程安全的,您应该将 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);
于 2013-08-26T16:55:51.060 回答
8

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

于 2013-08-26T16:28:25.053 回答
7
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();
     }
 }
于 2013-08-26T16:48:55.957 回答
7

一种替代方法是将您转换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());
}
于 2013-10-18T10:47:43.563 回答
2

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

这里

于 2014-04-16T13:23:21.140 回答