Sam
Sam

Reputation: 6890

Why ConcurrentModificationException occurred only at iterate loop

I wrote two example code as following:

private static class Person
{
    String name;

    public Person(String name)
    {
        this.name = name;
    }
}
public static void main(String[] args)
{
    List<Person> l = new ArrayList<Person>();
    l.add(new Person("a"));
    l.add(new Person("b"));
    l.add(new Person("c"));
    l.add(new Person("d"));


    for(int i  = 0;i<l.size();i++)
    {
        Person s = l.get(i);
        System.out.println(s.name);
        if(s.name.equals("b"))
            l.remove(i);
    }
    System.out.println("==========================");

    for(Person s : l)
        System.out.println(s.name);
}

When I run example code, print on console following result:

a
b
d
==========================
a
c
d

but when I change code as following by a iterate model:

  int i  = 0;
  for(Person s : l)
  {
      if(s.name.equals("b"))
          l.remove(i);
      i++;
  }

I get following result:

a
b
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)

Means of examples are: In traditional loop model ConcurrentModificationException not occurred at traditional for model??

Upvotes: 1

Views: 1052

Answers (3)

Thorsten Sch&#246;ning
Thorsten Sch&#246;ning

Reputation: 3707

Your second example hides an internally used iterator and if such an iteration is in place, you need to remove using the concrete iterator, not using the "global" remove method. In your first example you don't create an iterator, your are just accessing individual elemnts by some index, which is nothing the list cares about. But it does care about created, used, active iterators.

List.iterator Iterator.remove

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1499760

Yes, in your original for loop you're accessing the value by index - although you should note that you're skipping the check for element c (because by then the value that was at index 2 is now at index 1). There's no way this would create a ConcurrentModificationException, as there's no "context" between accesses - there's nothing to be invalidated when you modify the list.

In the enhanced-for-loop version, you're using an iterator which keeps the context of where you are within the collection. Calling l.remove invalidates that context, hence the error.

You can avoid it by calling remove on the iterator itself:

for (Iterator<Person> iterator = l.iterator(); iterator.hasNext(); ) {
    Person s = iterator.next();
    if (s.name.equals("b")) {
        iterator.remove();
    }
}

Upvotes: 2

JB Nizet
JB Nizet

Reputation: 691635

Because the foreach loop actually uses an iterator. It's equivalent to

for(Iterator<Person> it; it.hasNext(); ) {
    Person s = it.next();
    if(s.name.equals("b"))
        l.remove(i);
    i++;
}

And the iterators of standard collections like ArrayList, LinkedList, etc. fail when they detect that the collection has been modified between two calls to next().

The first loop doesn't use any iterator. It directly asks the list to get its nth element. This is fine for an ArrayList, but is extremely slow for a LinkedList, which has to traverse half of the list at every iteration, making the iteration O(n^2) instead of O(n).

Upvotes: 2

Related Questions