Categories
collections iteration java

Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop

1280

We all know you can’t do the following because of ConcurrentModificationException:

for (Object i : l) {
    if (condition(i)) {
        l.remove(i);
    }
}

But this apparently works sometimes, but not always. Here’s some specific code:

public static void main(String[] args) {
    Collection<Integer> l = new ArrayList<>();

    for (int i = 0; i < 10; ++i) {
        l.add(4);
        l.add(5);
        l.add(6);
    }

    for (int i : l) {
        if (i == 5) {
            l.remove(i);
        }
    }

    System.out.println(l);
}

This, of course, results in:

Exception in thread "main" java.util.ConcurrentModificationException

Even though multiple threads aren’t doing it. Anyway.

What’s the best solution to this problem? How can I remove an item from the collection in a loop without throwing this exception?

I’m also using an arbitrary Collection here, not necessarily an ArrayList, so you can’t rely on get.

1

1659

Iterator.remove() is safe, you can use it like this:

List<String> list = new ArrayList<>();

// This is a clever way to create the iterator and call iterator.hasNext() like
// you would do in a while-loop. It would be the same as doing:
//     Iterator<String> iterator = list.iterator();
//     while (iterator.hasNext()) {
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        // Remove the current element from the iterator and the list.
        iterator.remove();
    }
}

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.

Source: docs.oracle > The Collection Interface


And similarly, if you have a ListIterator and want to add items, you can use ListIterator#add, for the same reason you can use Iterator#remove — it’s designed to allow it.


In your case you tried to remove from a list, but the same restriction applies if trying to put into a Map while iterating its content.

16

  • 20

    What if you want to remove an element other than the element returned in the current iteration?

    – Eugen

    Apr 22, 2013 at 9:51

  • 2

    You have to use the .remove in the iterator and that is only able to remove the current element, so no 🙂

    – Bill K

    Apr 22, 2013 at 18:00

  • 1

    Be aware that this is slower compared to using ConcurrentLinkedDeque or CopyOnWriteArrayList (at least in my case)

    – Dan

    Oct 24, 2014 at 1:43

  • 1

    Is it not possible to put the iterator.next() call in the for-loop? If not, can someone explain why?

    – Blake

    Apr 12, 2016 at 13:22

  • 1

    @GonenI It’s implemented for all iterators from collections which aren’t immutable. List.add is “optional” in that same sense too, but you wouldn’t say it’s “unsafe” to add to a list.

    – Radiodef

    Aug 9, 2018 at 0:33


356

This works:

Iterator<Integer> iter = l.iterator();
while (iter.hasNext()) {
    if (iter.next() == 5) {
        iter.remove();
    }
}

I assumed that since a foreach loop is syntactic sugar for iterating, using an iterator wouldn’t help… but it gives you this .remove() functionality.

2

  • 48

    foreach loop is syntactic sugar for iterating. However as you pointed out, you need to call remove on the iterator – which foreach doesn’t give you access to. Hence the reason why you can’t remove in a foreach loop (even though you are actually using an iterator under the hood)

    – madlep

    Oct 21, 2008 at 23:30

  • 36

    +1 for example code to use iter.remove() in context, which Bill K’s answer does not [directly] have.

    – Eddified

    Oct 3, 2012 at 17:01

236

With Java 8 you can use the new removeIf method. Applied to your example:

Collection<Integer> coll = new ArrayList<>();
//populate

coll.removeIf(i -> i == 5);

6

  • 3

    Ooooo! I was hoping something in Java 8 or 9 might help. This still seems rather verbose to me, but I still like it.

    Sep 23, 2015 at 18:25

  • Is implementing equals() recommended in this case too?

    Dec 11, 2015 at 8:57

  • by the way removeIf uses Iterator and while loop. You can see it at java 8 java.util.Collection.java

    Oct 31, 2016 at 14:10


  • 3

    @omerhakanbilici Some implementations like ArrayList override it for performance reasons. The one you are referring to is only the default implementation.

    – Didier L

    Mar 31, 2017 at 8:33

  • @AnmolGupta: No, equals is not used at all here, so it doesn’t have to be implemented. (But of course, if you use equals in your test then it has to be implemented the way you want it.)

    – Lii

    Jan 3, 2019 at 10:31