首页 文章

如何在迭代时从“ArrayList”中删除元素时避免“ConcurrentModificationException”? [重复]

提问于
浏览
283

这个问题在这里已有答案:

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

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

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

10 回答

  • 6

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

    来自here

  • 484

    另一种方法是将 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());
    }
    
  • 56

    虽然其他建议的解决方案有效,但如果您真的希望解决方案成为线程安全的,您应该用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);
    
  • 70

    不不不!

    在单线程任务中,您不需要使用Iterator,而且还需要使用CopyOnWriteArrayList(由于性能损失) .

    解决方案更简单: try to use canonical for loop instead of for-each loop .

    根据Java版权所有者(几年前Sun,现在是Oracle)for-each loop guide,它使用迭代器来遍历集合并隐藏它以使代码看起来更好 . 但是,不幸的是,正如我们所看到的,它产生的问题多于利润,否则这个话题就不会出现 .

    例如,当在修改后的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关键字) .

  • 1

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

  • 154

    使用Iterator并调用remove()

    Iterator<String> iter = myArrayList.iterator();
    
    while (iter.hasNext()) {
        String str = iter.next();
    
        if (someCondition)
            iter.remove();
    }
    
  • 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();
         }
     }
    
  • 28

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

    for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
        iterator.next();
        if (someCondition) {
            iterator.remove();
        }
    }
    
  • 8

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

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

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

  • 6

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

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

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

相关问题