Calling remove in foreach loop in Java
Solution 1
To safely remove from a collection while iterating over it you should use an Iterator.
For example:
List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String s = i.next(); // must be called before you can call i.remove()
// Do something
i.remove();
}
From the Java Documentation :
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Perhaps what is unclear to many novices is the fact that iterating over a list using the for/foreach constructs implicitly creates an iterator which is necessarily inaccessible. This info can be found here
Solution 2
You don't want to do that. It can cause undefined behavior depending on the collection. You want to use an Iterator directly. Although the for each construct is syntactic sugar and is really using an iterator, it hides it from your code so you can't access it to call Iterator.remove
.
The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method.
Instead write your code:
List<String> names = ....
Iterator<String> it = names.iterator();
while (it.hasNext()) {
String name = it.next();
// Do something
it.remove();
}
Note that the code calls Iterator.remove
, not List.remove
.
Addendum:
Even if you are removing an element that has not been iterated over yet, you still don't want to modify the collection and then use the Iterator
. It might modify the collection in a way that is surprising and affects future operations on the Iterator
.
Solution 3
for (String name : new ArrayList<String>(names)) {
// Do something
names.remove(nameToRemove);
}
You clone the list names
and iterate through the clone while you remove from the original list. A bit cleaner than the top answer.
Solution 4
The java design of the "enhanced for loop" was to not expose the iterator to code, but the only way to safely remove an item is to access the iterator. So in this case you have to do it old school:
for(Iterator<String> i = names.iterator(); i.hasNext();) {
String name = i.next();
//Do Something
i.remove();
}
If in the real code the enhanced for loop is really worth it, then you could add the items to a temporary collection and call removeAll on the list after the loop.
EDIT (re addendum): No, changing the list in any way outside the iterator.remove() method while iterating will cause problems. The only way around this is to use a CopyOnWriteArrayList, but that is really intended for concurrency issues.
The cheapest (in terms of lines of code) way to remove duplicates is to dump the list into a LinkedHashSet (and then back into a List if you need). This preserves insertion order while removing duplicates.
Solution 5
I didn't know about iterators, however here's what I was doing until today to remove elements from a list inside a loop:
List<String> names = ....
for (i=names.size()-1;i>=0;i--) {
// Do something
names.remove(i);
}
This is always working, and could be used in other languages or structs not supporting iterators.
Michael Bobick
Updated on July 08, 2022Comments
-
Michael Bobick almost 2 years
In Java, is it legal to call remove on a collection when iterating through the collection using a foreach loop? For instance:
List<String> names = .... for (String name : names) { // Do something names.remove(name). }
As an addendum, is it legal to remove items that have not been iterated over yet? For instance,
//Assume that the names list as duplicate entries List<String> names = .... for (String name : names) { // Do something while (names.remove(name)); }
-
Mark McKenna over 12 yearsNot a great plan. Just because the language tolerates it on any given run, doesn't make it a good idea.
-
CurtainDog almost 12 yearsYou must've caught me in a bad mood, but seems to me the answer to this question comes straight out of the foreach documentation.
-
Sheldon over 11 years
-
ChenZhou over 11 yearsAs an alternative, you might consider not modifying your collection in-place but use a filtering combinator such as Guava's Iterables#filter: code.google.com/p/guava-libraries/wiki/FunctionalExplained Be aware of its lazy behavior!
-
cellepo over 5 yearsDid you really intend to ask about
Collection
specifically, rather thanList
which you use in your code? If you intended to ask aboutList
and notCollection
, then please edit this question to reflect that - then this question would not be a duplicate! (One big difference ofList
vsCollection
is thatList
includesget
in its interface, whileCollection
does not). -
Enginer almost 3 yearsYou can use removeIf method. Example: sockets.removeIf(Socket::isClosed);
-
-
Varun Mehta over 13 yearsWhy add the overhead of another list ?
-
Chathuranga Withana about 13 yearsSince the
for-each
loop hides theiterator
, you cannot call theremove()
directly. So in order to remove items from afor-each
loop while iterating through it, have to maintain a separate list. That list will maintain references to the items to be removed... -
Mark McKenna over 12 yearsJust as a side-note, that should work fine with any base-class list, but wouldn't be portable to more esoteric structures (like a self-sorting sequence, for example--in general, anywhere the ordinal for a given entry could change between list iterations).
-
mmatloka over 12 yearsIts very slow. removeAll 1. runs contains() on every toRemove element 2. then runs remove() which has to find element index so need to again look through whole list.
-
John Mellor about 12 yearsNote that you must call i.next() before you can call i.remove(): docs.oracle.com/javase/6/docs/api/java/util/Iterator.html
-
James P. almost 12 yearsI'm curious, why is this considered safe? Is the
Iterator
acting as middle man? -
Ethan Reesor almost 12 yearsI second James's question. What about that method is better?
-
Mark almost 12 yearsTo quote the Javadoc for Iterator.remove() "The behavior of an iterator is unspecified if the underlying collection is modified while the iteration is in progress in any way other than by calling this method." The iterator is acting as a middle man to safely perform the removal but allow the iteration to continue as expected.
-
Admin almost 11 yearsbut worth noting that the remove() method is OPTIONAL on an iterator and will throw exceptions if it isn't implemented for your particular collection or JVM
-
Admin almost 11 yearsthis isn't calling remove() in a foreach loop
-
shareef almost 11 yearsFYI you can call
it.next()
in if statment instead of the sample above -- or you can do as the code above -
Jake Toronto over 9 yearsNote: this works precisely because you're iterating backwords over the elements, so the indexes of the other remaining elements don't change when you remove the
i
th element. If you were looping from 0 tosize()-1
, and doing the remove, you'd see the issues mentioned in other answers. Nice job, though! -
committedandroider about 9 yearsI get the idea but based on isn't this all happening in one thread though? ConcurrentModificationException would make more sense if there was thread acting on the data structure.
-
Mark about 9 years@committedandroider From the Javadoc for ConcurrentModificationException
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
-
itzhar over 8 yearsthis supposed to be the first answer here...
-
D3V over 8 yearsBeware. For composite objects with no equals and hashCode method, it might just fail. However, marked answer safely removes it.
-
DavidR over 7 yearsThis is just a horrible way to do. So many things can go wrong depending on the implementation of the List (or Set, etc.).
-
TheArchon over 7 yearsSuch indexed based operations should only be conducted on lists whose elements can be accessed in O(1) constant time. If the list is not randomly accessible (does not implement RandomAccess) then you should use an iterator instead, as these types of collections usually take longer to retrieve an element at a specific index position, such as a LinkedList.
-
Sumit Gupta about 7 years@Mark, But why java has designed only iterator to remove element. This could have been done for each loop also?
-
forresthopkinsa over 6 yearsAs long as we're working with Java collections, where we do have iterators, there is no advantage to this method -- it only adds potential for really difficult debugging
-
Serafeim over 6 yearsThe advantage of the above method is that you (or at least most people) don't have to google for "java iterator example" but can write it immediately, by memory.
-
SND over 6 yearsThough short and clean, if performance/memory usage are an issue, it is worth to note that this solution runs in O(n²) and creates a copy of the original list, which requires memory and an operation depending on the type of the list. Using an iterator over a LinkedList you can bring complexity down to O(n).
-
cellepo over 5 yearsAnalogous code that iterates from beginning (increments
i
instead conditionally in body, which I believe alleviates most if not all other concerns). -
cellepo over 5 yearsEither way, these types of Answers exhibit that it's particularly the enhanced-for-loop that will throw
ConcurrentModificationException
- not the traditional-for-loop (although as mentioned, extra care must be taken with its incrementing). -
cellepo over 5 yearsAlthough it will avoid
ConcurrentModificationException
, there could still be other indexing problems/Exceptions (more details). -
cellepo over 5 yearsAlthough
CopyOnWriteArrayList
will avoidConcurrentModificationException
, there could still be other indexing problems/Exceptions (more details). -
FINDarkside about 5 years@SND Why would it be n^2? Creating copy of the list is O(n), therefore this is O(n) as well.
-
chakwok almost 5 years@FINDarkside the O(n^2) doesn't come from creating a copy, but looping through the ArrayList (O(n)) while calling remove() on each element (also O(n)).
-
pratpor over 4 yearsThumbs up for the extra Note *that the code calls Iterator.remove, not List.remove". I almost missed and used list.remove
-
Serafins over 4 yearsas of Java 8 you can actually use: names.removeIf( t -> t <some condition>)