如果我们在迭代时在ArrayList中添加元素,则抛出ConcurrentModificationException

时间:2017-03-03 09:45:20

标签: java collections

我正在使用Iteratot迭代ArrayList的元素。重复迭代我最后在ArrayList中添加了一个元素。但是收到错误:ConcurrentModificationException。

如果我将ArrayList更改为Set并尝试在迭代期间在set中添加元素,则不会抛出错误

Test.java

class Test {
  public static void main() {
    ArrayList<Integer> al = new ArrayList<Integer>();
    al.add(20);
    al.add(30);
    al.add(40);

    Iterator<Integer> it = marks.iterator();
    while (it.hasNext()) {
      int value = it.next();
      if (value == 40) al.add(50);
    }
  }
}

Test2.java

class Test2 {
  public static void main() {
    Set<Integer> marks = new HashSet<>();
    marks.add(20);
    marks.add(30);
    marks.add(40);

    Iterator<Integer> it = marks.iterator();
    while (it.hasNext()) {
      int value = it.next();
      if (value == 40) marks.add(50);
    }
  }
}

3 个答案:

答案 0 :(得分:4)

可能有一个更好的解决方案,但这个解决方案也是如此。在循环结束后,使用第二个列表,然后将其添加到原始列表中。

主要

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Integer> al = new ArrayList<Integer>();
        List<Integer> toAdd = new ArrayList<Integer>();
        int stepSize = 10;

        al.add(20);
        al.add(30);
        al.add(40);

        Iterator<Integer> it = al.iterator();
        while (it.hasNext()) {
            int value = it.next();
            if (value == al.get(al.size() - 1)){
                toAdd.add(value + stepSize);
            }
        }

        // Add all elements
        al.addAll(toAdd);

        // Print
        al.forEach(System.out::println);
    }
}

如果你只想根据最后一个值附加一个元素,就像在你的例子中一样,你也可以等到循环完成(如果你仍然需要它)并尝试添加它是这样的:

al.add(al.get(al.size() - 1) + stepSize);

<强>输出

20
30
40
50

答案 1 :(得分:1)

仅凭ConcurrentModificationException HashSet获得HashSet仅仅是侥幸。这种侥幸的原因是你碰巧在循环的 last 迭代中添加了元素。

这远非保证发生,因为ConcurrentModificationException没有排序保证。尝试在列表中添加或删除更多元素,您会发现最终能够获得40,因为HashSet不会是列表中的最后一个元素。

重现这一点的最简单方法是只使用一个元素(从那时起HashSet<Integer> hashSet = new HashSet<>(); hashSet.add(1); for (int i : hashSet) { hashSet.add(2); } 的排序已知):

ConcurrentModificationException

这不会抛出ArrayList<Integer> list = new ArrayList<>(); list.add(1); for (int i : list) { list.add(2); }

ConcurrentModificationException

抛出HashSet

关于在最后一次迭代中添加abstract class HashIterator { // ... public final boolean hasNext() { return next != null; } // ... } 时没有得到异常的原因, 查看HashSet.iterator()返回的迭代器的源代码:

hasNext()

因此,next值是基于 final Node<K,V> nextNode() { Node<K,V>[] t; Node<K,V> e = next; if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (e == null) throw new NoSuchElementException(); if ((next = (current = e).next) == null && (t = table) != null) { do {} while (index < t.length && (next = t[index++]) == null); // ^ Here } return e; } 预先计算的;这在代码中设置得稍低:

hasNext

因此,当确定迭代的前一个元素时,确定Iterator it = marks.iterator(); while (it.hasNext()) { int value = it.next(); if (value == 40) marks.add(50); } 的值。

因为您的代码是:

hasNext()

在循环迭代之后调用next,其中添加了元素;但在调用it.next()之前add被设置为空 - 在调用hasNext()之前 - 因此ConcurrentModificationException将返回false,并且循环退出时不会hasNext()

另一方面,iterator of ArrayList使用基础列表的大小来确定 public boolean hasNext() { return cursor != size; // ^ current position of the iterator // ^ current size of the list } 的返回值:

hasNext()

因此,就增加(或减少)基础列表的大小而言,此值是“实时”的。因此,next()在添加值后将返回true;因此调用了next()。但 public E next() { checkForComodification(); // ... 做的第一件事就是检查编纂:

ConcurrentModificationException

因此检测到更改,并抛出ConcurrentModificationException

请注意,如果您在最后一次迭代中修改了,那么获得ArrayList <div class="row" *ngFor="let contact of contacts"> <!-- Here I want to loop 4 of the contacts before rendering a new row --> <div class="col-md-3"> <contact-card [contact]="contact"></contact-card> </div> </div> ,如果您要添加并删除一个元素(或者更确切地说,添加和删除相同数量的元素,以便之后列表的大小相同)。

答案 2 :(得分:0)

是的,这是正常列表的预期。

您可以将CopyOnWriteArrayList用于列表上的并发活动。

例如,click here