65
@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.

看起来好像当我从 中删除指定元素时listlist不知道它size已被更改。

我想知道这是否是collections删除元素的常见问题?

4

11 回答 11

95

我相信这是Iterator.remove()方法背后的目的,能够在迭代时从集合中删除一个元素。

例如:

Iterator<String> iter = li.iterator();
while(iter.hasNext()){
    if(iter.next().equalsIgnoreCase("str3"))
        iter.remove();
}
于 2011-02-25T02:52:40.500 回答
32

从没有迭代器的列表中删除它的 Java 8 方法是:

li.removeIf(<predicate>)

IE

List<String> li = new ArrayList<String>();
// ...
li.removeIf(st -> !st.equalsIgnoreCase("str3"));
于 2014-11-09T18:06:22.463 回答
19

请注意,此异常并不总是表示对象已被不同的线程同时修改。如果单个线程发出一系列违反对象约定的方法调用,则该对象可能会抛出此异常。例如,如果线程在使用快速失败迭代器迭代集合时直接修改集合,则迭代器将抛出此异常

取自http://download.oracle.com/javase/1.4.2/docs/api/java/util/ConcurrentModificationException.html

于 2011-02-25T02:45:12.340 回答
6

是的,人们遇到了它——问题是你不能在迭代列表时修改它。我过去使用过 2 种替代方法:

  1. 您可以跟踪要删除的项目的索引,然后在完成迭代后将其删除。
  2. 或者,您可以在迭代时将所有要保留的列表复制到新列表中,然后在完成后丢弃旧列表。

这些选项假定您必须遍历列表以查找要删除的元素——在列表元素是具有您可能测试的属性的复杂对象的情况下很有用。

在您的特定情况下,您甚至不需要迭代,因为您可以使用 removeAll。看看这里的 API 。还有一些漂亮的方法,比如 retainAll 会丢弃所有不在参数中的东西。只要列表中的对象正确实现等于和哈希码,您就可以使用类似删除/保留的方法。如果您不能依靠 equals/hashcode 来识别应用程序中实例之间的相等性,则必须自己进行删除....

于 2011-02-25T02:44:56.677 回答
4

试试这个(Java 8):

list.removeIf(condition);
于 2017-11-01T13:59:35.087 回答
3

您可以直接在 for-each 循环中复制要从中删除元素的列表。对我来说,这是最简单的方法。像这样的东西:

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

希望对你有帮助。。

于 2017-06-14T13:40:09.390 回答
2

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

@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);
}
于 2014-04-18T17:26:37.063 回答
1

ArrayList 具有字段modCount- 集合修改计数

当您调用方法时iterator()会创建新对象Itr。它有字段expectedModCount。字段按值expectedModCount 初始化 。modCount当你调用

li.remove("str3");

modCount增量。您何时尝试li通过迭代器访问检查 expectedModCount == modCount

如果它是错误的抛出ConcurrentModificationException

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

于 2014-03-11T18:39:09.373 回答
1

我认为最佳答案来自 bigdev.de,但我想向其中添加一些内容(例如,如果该项目已从列表中删除,也许您想在某处或某处记录该项目):

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;
  });
于 2017-07-05T12:52:53.020 回答
0

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

或者,您可以在迭代时将所有要保留的列表复制到新列表中,然后在完成后丢弃旧列表。

@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);
}
于 2012-12-05T06:45:09.393 回答
0

我用另一种方式循环...

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);
}
于 2017-02-15T03:32:07.297 回答