Thommy
Thommy

Reputation: 5417

Remove multiple items from ArrayList during iterator

Is it safe to remove multiple items from an ArrayList while iterating through it with an iterator?

Iterator<String> iterator = nameList.iterator();
 while(iterator.hasNext()){
     String s = iterator.next();
     List<String> list = work(s);
     for (String s1 : list) {
        nameList.remove(s1);   
    }
}

The work() method gives back a list of names that should be removed from the nameList, during the runtime of while loop.

Upvotes: 7

Views: 2097

Answers (4)

Curtis Yallop
Curtis Yallop

Reputation: 7329

You can use Iterator.remove() (as opposed to nameList.remove(s)).

Iterator<String> iterator = nameList.iterator();
while(iterator.hasNext()){
    String s = iterator.next();
    iterator.remove();
}

Also consider nameList.clear() and nameList.removeAll(elementsToRemove).

Iterators are generally invalid after any modification to their underlying collections, except via the iterator itself. - When I sort a List what happens to its iterators?

https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html

Upvotes: 0

Jayanth
Jayanth

Reputation: 816

You can implement your logic if you use a ListIterator which is fail-safe. Below is a basic example :

Set<String> removed = new HashSet<>();
    ArrayList<String> nameList = new ArrayList<String>();

            ListIterator<String> iterator = nameList.listIterator();
             while(iterator.hasNext()){
                 String s = iterator.next();
                 if (!removed.contains(s)) {
        removed.addAll(work(s));
    }

            }


 nameList.removeAll(removed);
                 System.out.println(nameList);

With your logic, you have to account the performance. If performance is not a factor, you can go ahead and add/remove from a list through ListIterator.

Upvotes: 2

senerh
senerh

Reputation: 1365

As a previous answer said, removing an item from a iterated list is not safe. Here is a code that throws ConcurrentModificationException:

List<String> list = new ArrayList<>(Arrays.asList("1", "2", "3"));
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    list.remove("1");
}

Upvotes: 0

Eran
Eran

Reputation: 394126

No, it's not safe, and can throw ConcurrentModificationException. You can collect all the elements to be removed in a temporary List, and then call list.removeAll(tmpList) after the while loop to perform the removal.

Iterator<String> iterator = nameList.iterator();
List<String> removed = new ArrayList<>();
while(iterator.hasNext()){
    String s = iterator.next();
    removed.addAll(work(s));
}
list.removeAll(removed);

I realize this can be less efficient, since you might be calling work(s) on Strings the should have been removed from the List earlier. This can be improved by changing tempList to a Set, and only calling work(s) for Strings not in the Set:

Iterator<String> iterator = nameList.iterator();
Set<String> removed = new HashSet<>();
while(iterator.hasNext()){
    String s = iterator.next();
    if (!removed.contains(s)) {
        removed.addAll(work(s));
    }
}
list.removeAll(removed);

Upvotes: 12

Related Questions