在列表迭代期间从java.util.List中删除元素时抛出ConcurrentModificationException?

Getting a ConcurrentModificationException thrown when removing an element from a java.util.List during list iteration?

本问题已经有最佳答案,请猛点这里访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testListCur(){
    List<String> li=new ArrayList<String>();
    for(int i=0;i<10;i++){
        li.add("str"+i);
    }

    for(String st:li){
        if(st.equalsIgnoreCase("str3"))
            li.remove("str3");
    }
    System.out.println(li);
}

运行此代码时,将抛出ConcurrentModificationException。

当我从列表中删除指定的元素时,列表似乎不知道其大小已更改。

我想知道这是否是集合和移除元素的常见问题?


我相信这是迭代器.remove()方法背后的目的,以便能够在迭代时从集合中移除元素。

例如:

1
2
3
4
5
Iterator<String> iter = li.iterator();
while(iter.hasNext()){
    if(iter.next().equalsIgnoreCase("str3"))
        iter.remove();
}


Java 8从Iterator中删除列表的方法是:

1
li.removeIf([cc lang="java"])

1
2
3
List<String> li = new ArrayList<String>();
// ...
li.removeIf(st -> !st.equalsIgnoreCase("str3"));


Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will thow this exception

摘自http://download.oracle.com/javase/1.4.2/docs/api/java/util/concurrentmodificationexception.html


是的,人们会碰到它——问题是在迭代列表时不能修改它。我过去使用过两种选择:

  • 您可以跟踪要删除的项的索引,然后在迭代完成后删除它们。
  • 或者,您可以在迭代时将要保留的所有列表复制到新列表中,然后在完成后丢弃旧列表。
  • 这些选项假定您必须遍历列表以查找要删除的元素——在列表元素是具有您可能测试的属性的复杂对象的情况下非常有用。

    在您的特定情况下,您甚至不需要迭代,因为您只需要使用removeall。看看这里的API。还有一些漂亮的方法,如retinallal,可以丢弃论点中没有的所有内容。只要列表中的对象正确实现equals和hashcode,就可以使用remove/retain-like方法。如果您不能依赖equals/hashcode来标识应用程序中实例之间的相等性,则必须自己进行删除….


    尝试这个(Java 8):

    1
    list.removeIf(condition);

    您可以为每个循环直接创建一个要从中删除元素的列表的副本。对我来说,这是最简单的方法。像这样:

    1
    2
    3
    for (String stringIter : new ArrayList<String>(myList)) {
        myList.remove(itemToRemove);
    }

    希望能帮到你……


    我认为值得一提的是Java 8版本。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Test
    public void testListCur() {
        List<String> li = new ArrayList<String>();
        for (int i = 0; i < 10; i++) {
            li.add("str" + i);
        }

        li = li.stream().filter(st -> !st.equalsIgnoreCase("str3")).collect(Collectors.toList());

        System.out.println(li);
    }


    arraylist具有字段modCount—集合修改计数

    当调用方法iterator()时,创建新的对象Itr。它有场expectedModCountexpectedModCount字段由modCount值初始化。当你调用

    1
    li.remove("str3");

    modCount增量。你什么时候尝试通过迭代器访问li?检查EDOCX1[8]

    如果是假的话,就把它扔出去。

    因此,如果您得到迭代器,并且在集合修改之后,迭代器被认为是无效的,并且您不能使用它。


    我遇到了这个问题,我认为更简单的方法和hvgotcodes给出的第二种方法相同。

    Or you can copy all the ones you want to keep into a new list as you iterate, and then discard the old list when done.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @Test
    public void testListCur(){
        List<String> li=new ArrayList<String>();
        for(int i=0;i<10;i++){
            li.add("str"+i);
        }
        List<String> finalLi = new ArrayList<String>();
        for(String st:li){
            if(st.equalsIgnoreCase("str3")){
                // Do nothing
            } else {
                finalLi.add(st);
            }
        }
        System.out.println(finalLi);
    }

    我认为最好的答案来自bigdev.de,但我想在其中添加一些内容(例如,如果从列表中删除该项,可能您希望将其记录到某个位置或其他位置):

    1
    2
    3
    4
    5
    6
    7
    8
    List<String> list = new ArrayList<>();

    list.removeIf(a -> {
                    boolean condition = a.equalsIgnoreCase("some condition");
                    if(condition)
                        logger.info("Item removed from the list:" + a);
                    return condition;
      });

    我以不同的方式循环…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public void testListCur(){
        List<String> li=new ArrayList<String>();
        for(int i=0;i<10;i++){
            li.add("str"+i);
        }

        for(int i=0; i<li.size(); i++)
            if(li.get(i).equalsIgnoreCase("str3"))
                li.remove(i--);

        System.out.println(li);
    }