2

I've stumbled upon this code, which throws a ConcurrentModificationException

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));

for (String s : list) {
    if (s.equals("a"))
        list.remove(s);
}

If you add an Iterator and use a while-loop, the code works fine:

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();

    if (s.equals("a")) {
        iter.remove();
    }
}

I dont understand, why it is necessary, to use Iterator<String> in this case. Is it because of the ArrayList not having some sort of ability to get iterated, altough it is a subclass of Collection?

Is it necessary to use the while-loop, or could you produce a version with a for-loop?

hamena314
  • 2,969
  • 5
  • 30
  • 57
  • This might be useful: http://stackoverflow.com/questions/1196586/calling-remove-in-foreach-loop-in-java – Boris Jan 15 '16 at 11:53
  • From http://docs.oracle.com/javase/tutorial/collections/interfaces/collection.html : Iterator.remove is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress. – Boris Jan 15 '16 at 11:56

4 Answers4

7

Your link actually explains it pretty well:

In the foreach loop, compiler will make the .next() called after the operation of removing element, which caused the ConcurrentModificationException.

R.Costa
  • 1,395
  • 9
  • 16
7

If a Collection is modified while iterating over it, in most of the implementations, a ConcurrentModificationException is thrown.

The "foreach" version:

for (String s : list) {
    if (s.equals("a"))
        list.remove(s);
}

internally is equivalent to

for(Iterator<String> i = list.iterator(); i.hasNext(); ) {
    String s = i.next();
    if (s.equals("a"))
        list.remove(s);
}

As you can see, an interator is created, but the list is modified directly. The list can only be modified by the iterator i used to iterate over it.

Tobías
  • 6,142
  • 4
  • 36
  • 62
3

The for uses an iterator internally. But you delete on the list directly => CME. The internal call to next after the deletion will throw it because it finds the list modified.

In the example with while, you delete through the iterator, which works fine.

Fildor
  • 14,510
  • 4
  • 35
  • 67
1

When removing object from collections, use ITERATORS

Your code does not work because you are modifying the collection while looping over it. According to Oracle's javadoc the only safe way to do it is using iterators.

Note that Iterator.remove() is the only safe way to modify a collection during iteration; the behavior is unspecified if the underlying collection is modified in any other way while the iteration is in progress.

Have a look at this for further infos

Davide Spataro
  • 7,319
  • 1
  • 24
  • 36