如何"删除"函数适用于ArrayList,同时使用for-each循环进行迭代?

时间:2014-04-19 14:00:44

标签: java arraylist foreach

我有一个非常基本的问题。

我创建了简单的ArrayList,我在使用for-each循环迭代时删除了该项。它给了我java.util.ConcurrentModificationException,因为我在迭代时无法移除项目,但当我取消评论if条件时,它可以正常工作

任何人都可以通过这种方式向我解释每个人的工作方式。

    ArrayList<String> list1 = new ArrayList<String>();
    list1.add("Hello");
    list1.add("World");
    list1.add("Good Evening");

    for (String s : list1) {
        //if (s.equals("World")) {
            list1.remove(1);
        //}
    }

如果我将其更改为list1.remove(2);list1.remove(0);,那么其工作正常。

注意:这是示例代码,我知道它可以使用Iterator正常工作。这个问题的唯一目的是知道方法remove()如果条件未被注释,无论你从列表中删除什么索引,如何完美地工作。

3 个答案:

答案 0 :(得分:5)

该列表有一个名为modCount的变量,表示&#34;修改计数&#34;。每当您致电remove(或执行其他结构修改)时,它会递增modCount

如果要在不告知迭代器的情况下添加或删除元素,迭代器就无法跟踪其在列表中的位置。因此,作为安全检查,在迭代开始时,迭代器会记录modCount,并将其保存为expectedModCount。当从迭代器中读取每个项时,迭代器会检查以确保modCount仍然等于期望值,并且如果它没有,则抛出异常。

通常,如果在迭代期间不安全地修改列表,这将成功导致抛出异常。但是,在启用if语句的情况下,这还不够。在您的代码阅读"World"后,该项目将被删除,因此该列表现在包含["Hello", Good Evening"]。迭代器仍然在位置1(现在包含"Good Evening"),当它尝试读取下一个项目时,它发现它现在已经到达列表的末尾,所以它不会打扰检查modCount 。因此,也不例外。

请注意ConcurrentModificationException documentation中的警告:&#34;一般来说,在存在非同步并发修改的情况下,无法做出任何硬保证。失败快速操作会尽最大努力抛出ConcurrentModificationException。&#34;

即使在这种情况下没有发生抛出异常,代码仍然是错误的。要在迭代时删除元素,必须使用迭代器自己的remove方法:

for (Iterator<String> it = list1.iterator(); it.hasNext();) {
    String s = it.next();
    if (s.equals("World")) {
        it.remove();
    }
}

这样,迭代器知道列表已经改变,仍然可以正确迭代。

或者,您可以从列表的临时副本进行迭代:

for (String s : new ArrayList<>(list1)) {
    if (s.equals("World")) {
        list1.remove(...);
    }
}

虽然在这个简单的例子中,你甚至不需要这样做;你可以写:

list1.remove("World");

答案 1 :(得分:0)

您还可以使用基于索引的删除。此解决方案的缺点是在每次循环迭代期间都会对list1.size()进行求值。积极的一点是,通过索引从List中删除项目的速度更快。

for (int i = 0; i < list1.size(); /* i incremented in loop body */) {
  if ("World".equals(list1.get(i))) {
    list1.remove(i);
  }
  else {
    i++;
  }
}

答案 2 :(得分:0)

使用Iterator并调用remove():

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

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

    if (someCondition)
        iter.remove();
}