398

I'm trying to remove some elements from an ArrayList while iterating it like this:

for (String str : myArrayList) {
    if (someCondition) {
        myArrayList.remove(str);
    }
}

Of course, I get a ConcurrentModificationException when trying to remove items from the list at the same time when iterating myArrayList. Is there some simple solution to solve this problem?

ʇolɐǝz ǝɥʇ qoq
  • 717
  • 1
  • 15
  • 30
Ernestas Gruodis
  • 8,567
  • 14
  • 55
  • 117

10 Answers10

633

Use an Iterator and call remove():

Iterator<String> iter = myArrayList.iterator();

while (iter.hasNext()) {
    String str = iter.next();

    if (someCondition)
        iter.remove();
}
arshajii
  • 127,459
  • 24
  • 238
  • 287
  • 7
    Thanks, now everything works fine :) I think this answer is the best, because the code is easily readable. – Ernestas Gruodis Aug 26 '13 at 16:40
  • 6
    @ErnestasGruodis The tradeoff is that iter is now in scope for the rest of the method. – Eric Stein Aug 27 '13 at 10:36
  • 4
    What if I wanted to remove something other than the current iteration (say it's on index 2, but I need to remove index 7 at the same time). It gives me a ConcurrentModificationException whenever I try through .remove(index). – user1433479 Jan 12 '15 at 21:40
  • 52
    Funny, I got same exception on `String str = iter.next();` ! Java with collections sucks ! – Al-Mothafar Jan 17 '15 at 14:23
  • 1
    iterator initializer may be put in the first section of a c-style for-loop expression. – Erkin Alp Güney May 15 '16 at 12:45
  • What about this, Collections.unmodifiableList – Hamedz Jun 14 '16 at 08:45
  • 3
    Got the same exception using this approach as well. – Aakash Patel Jun 08 '18 at 05:46
  • 1
    @AakashPatel Then it's a different problem. – arshajii Jun 09 '18 at 15:02
  • For me not work. I use this .ArrayList f = (ArrayList) list.clone(); f.remove(onMessage); list = f; – Ali Bagheri Nov 25 '18 at 08:13
  • @Al-Mothafar do a type casting to that statement it will work fine. When ever converting higher to lower type always use typecasting i.e; `String str = (String) iter.next();` – Hemanth Peela Jan 30 '19 at 10:29
  • @Al-Mothafar (and other): the problem is not Java collections (which are great) but your multi-threaded program where a thread modifies the list that is being iterated in another thread. – Matthieu May 11 '20 at 13:33
  • ***Iterators*** are not always helpful when another thread modifies the collection. I had tried many ways but then I realized traversing the collection manually is much safer (backward for removal) – Saeed Ir Nov 23 '22 at 06:58
222

As an alternative to everyone else's answers I've always done something like this:

List<String> toRemove = new ArrayList<String>();
for (String str : myArrayList) {
    if (someCondition) {
        toRemove.add(str);
    }
}
myArrayList.removeAll(toRemove);

This will avoid you having to deal with the iterator directly, but requires another list. I've always preferred this route for whatever reason.

Eugene
  • 10,627
  • 5
  • 49
  • 67
Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
  • 28
    +1 I like this iteratorless solution. – Terry Li Aug 26 '13 at 16:39
  • 5
    @KevinDiTraglia Is there some reason to use more resources than you need? It's not like iterators are that hard to work with or make the code messy. – Eric Stein Aug 26 '13 at 16:45
  • 2
    @EricStein I usually end up in situations where I want to add to the list as well, and the extra resources are mostly trivial. It's just an alternative solution, both have their pros and cons. – Kevin DiTraglia Aug 26 '13 at 16:47
  • @KevinDiTraglia I agree that the resources are usually negligible. – Eric Stein Aug 26 '13 at 16:52
  • 4
    @EricStein If we go an extra step and use immutable lists (like those in the Guava library) then this becomes much more attractive when dealing with multithreaded concurrency issues. – Nobbynob Littlun Aug 19 '14 at 18:34
  • I'm really sorry I accidentally downvoted this without noticing and now I can't change it. – Script Kitty Mar 22 '17 at 13:10
  • This is the slowest possible way. – Faraz Jun 11 '17 at 07:50
  • @KevinDiTraglia really appreciate the way you thinking – Nibras Oct 23 '18 at 06:24
  • What is wrong with using an Iterator? That said, I like this approach because it does not manipulate state, but creates a new one. Feels kind a immutable ;-) – Christian May 28 '19 at 15:43
  • @KevinDiTraglia any idea about the performance of this method against a method which using iterator? BTW this is coolest solution – Nibras Sep 17 '19 at 11:09
106

Java 8 user can do that: list.removeIf(...)

    List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
    list.removeIf(e -> (someCondition));

It will remove elements in the list, for which someCondition is satisfied

Mikhail Boyarsky
  • 2,908
  • 3
  • 22
  • 37
70

You have to use the iterator's remove() method, which means no enhanced for loop:

for (final Iterator iterator = myArrayList.iterator(); iterator.hasNext(); ) {
    iterator.next();
    if (someCondition) {
        iterator.remove();
    }
}
sa2000
  • 33
  • 7
Eric Stein
  • 13,209
  • 3
  • 37
  • 52
  • 10
    I feel this answer communicates better; the iterator is confined to the for-loop, and the details of the iteration are in the for statement. Less visual noise. – Nobbynob Littlun Aug 19 '14 at 18:26
  • If you add type parameters to Iterator and assign that iterator.next() to some variable so you can actually do something with it in the if this is the best solution – sscarduzio Apr 15 '17 at 10:16
  • Why you declare the iterator as final ? – kh.tab Apr 19 '17 at 08:55
  • 1
    @kh.tab I consider it a good habit to declare as final all variables that are not intended to be reassigned. I only wish "final" were the default. – Eric Stein Apr 19 '17 at 14:27
42

No, no, NO!

In single threated tasks you don't need to use Iterator, moreover, CopyOnWriteArrayList (due to performance hit).

Solution is much simpler: try to use canonical for loop instead of for-each loop.

According to Java copyright owners (some years ago Sun, now Oracle) for-each loop guide, it uses iterator to walk through collection and just hides it to make code looks better. But, unfortunately as we can see, it produced more problems than profits, otherwise this topic would not arise.

For example, this code will lead to java.util.ConcurrentModificationException when entering next iteration on modified ArrayList:

        // process collection
        for (SomeClass currElement: testList) {

            SomeClass founDuplicate = findDuplicates(currElement);
            if (founDuplicate != null) {
                uniqueTestList.add(founDuplicate);
                testList.remove(testList.indexOf(currElement));
            }
        }

But following code works just fine:

    // process collection
    for (int i = 0; i < testList.size(); i++) {
        SomeClass currElement = testList.get(i);

        SomeClass founDuplicate = findDuplicates(currElement);
        if (founDuplicate != null) {
            uniqueTestList.add(founDuplicate);
            testList.remove(testList.indexOf(currElement));
            i--; //to avoid skipping of shifted element
        }
    }

So, try to use indexing approach for iterating over collections and avoid for-each loop, as they are not equivalent! For-each loop uses some internal iterators, which check collection modification and throw ConcurrentModificationException exception. To confirm this, take a closer look at the printed stack trace when using first example that I've posted:

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)
    at TestFail.main(TestFail.java:43)

For multithreading use corresponding multitask approaches (like synchronized keyword).

Dima Naychuk
  • 463
  • 4
  • 4
  • 19
    Worth noting that given the way a LinkedList works internally, an Iterator is *far* more efficient than subsequent `get(i)` calls with an incremented `i`. – Angad Aug 17 '15 at 11:12
  • Great comment and details, thanks – OlivierM Feb 20 '16 at 00:45
  • 1
    Agreed with Angad. One often only has access to the generic List type, and the implementation that was used is then unknown. If the implementation used is LinkedList, using a C style for loop to loop through the List retrieving each one will result in O(n2) complexity. – mdewit Mar 30 '16 at 19:29
  • 7
    You can avoid the `i--; //to avoid skipping of shifted element` by looping downwards: `for (int i = testList.size()-1; i >= 0; i--) { ... }` Also, instead of `testList.remove(testList.indexOf(currElement));` you can simply write `testList.remove(i);` – Martin Rust Nov 15 '16 at 14:59
  • @Angad But using of iterator causes exception mentioned, due to it relies on previous-current-next relation, which is broken in case of removing an element from the collection. Here we should pay by performance hit. – Dima Naychuk May 08 '17 at 14:54
  • @MartinRust Yes, thank you for comments! – Dima Naychuk May 08 '17 at 14:58
9

While other suggested solutions work, If you really want the solution to be made thread safe you should replace ArrayList with CopyOnWriteArrayList

    //List<String> s = new ArrayList<>(); //Will throw exception
    List<String> s = new CopyOnWriteArrayList<>();
    s.add("B");
    Iterator<String> it = s.iterator();
    s.add("A");

    //Below removes only "B" from List
    while (it.hasNext()) {
        s.remove(it.next());
    }
    System.out.println(s);
Prashant Bhate
  • 10,907
  • 7
  • 47
  • 82
  • 2
    Yes, but Java documentation says that "This is ordinarily too costly, but may be more efficient than alternatives when traversal operations vastly outnumber mutations, and is useful when you cannot or don't want to synchronize traversals, yet need to preclude interference among concurrent threads." – Ernestas Gruodis Aug 26 '13 at 17:05
8

If you want to modify your List during traversal, then you need to use the Iterator. And then you can use iterator.remove() to remove the elements during traversal.

Juned Ahsan
  • 67,789
  • 12
  • 98
  • 136
7
List myArrayList  = Collections.synchronizedList(new ArrayList());

//add your elements  
 myArrayList.add();
 myArrayList.add();
 myArrayList.add();

synchronized(myArrayList) {
    Iterator i = myArrayList.iterator(); 
     while (i.hasNext()){
         Object  object = i.next();
     }
 }
Prabhakaran Ramaswamy
  • 25,706
  • 10
  • 57
  • 64
  • 2
    In this answer where are you removing the item(s) from list? OP asked How to avoid “ConcurrentModificationException” while removing elements. I could not see any reason why others up-voted this answer. – Shailesh Saxena Oct 30 '18 at 12:40
7

One alternative method is convert your List to array, iterate them and remove them directly from the List based on your logic.

List<String> myList = new ArrayList<String>(); // You can use either list or set

myList.add("abc");
myList.add("abcd");
myList.add("abcde");
myList.add("abcdef");
myList.add("abcdefg");

Object[] obj = myList.toArray();

for(Object o:obj)  {
    if(condition)
        myList.remove(o.toString());
}
Allan Pereira
  • 2,572
  • 4
  • 21
  • 28
CarlJohn
  • 727
  • 2
  • 9
  • 20
  • 1
    Why is there a object.toString() while removing? shouldn't it just be 'o'? – themorfeus Feb 11 '14 at 20:02
  • @TheMorfeus It can be just 'o'. But I used toString() method to avoid 'Suspicious method call' bug from IDE. No other specific reasons. – CarlJohn Feb 12 '14 at 09:27
  • 2
    This solution is suitable only for small size of list. Just imagine a list containing thousands of items, converting to array will be very costly. – Shailesh Saxena Oct 30 '18 at 12:43
2

You can use the iterator remove() function to remove the object from underlying collection object. But in this case you can remove the same object and not any other object from the list.

from here

Gilo
  • 640
  • 3
  • 23