Why is using the for-each loop on an ArrayList not working?

13,332

Solution 1

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.

Solution 2

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.

Solution 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.

Solution 4

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

Share:
13,332
hamena314
Author by

hamena314

"What have I done? What did I expect? What happened?" - Formula for writing a good question Effort for answer = readability + interestingness + effort for research shown

Updated on June 13, 2022

Comments

  • hamena314
    hamena314 almost 2 years

    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?

  • Fildor
    Fildor over 8 years
    you are missing a call to next :)
  • Tobías
    Tobías over 8 years
    You're right, thanks!
  • hamena314
    hamena314 over 8 years
    By modifying do you mean only remove or would adding elements have the same problem?
  • Fildor
    Fildor over 8 years
    @hamena314 Not necessarily a Problem when adding, but you cannot be sure. Implementations may vary. So using the iterator to modify is the only guaranteed safe way.