Calling remove in foreach loop in Java

510,295

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.

Share:
510,295
Michael Bobick
Author by

Michael Bobick

Updated on July 08, 2022

Comments

  • Michael Bobick
    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
      Mark McKenna over 12 years
      Not a great plan. Just because the language tolerates it on any given run, doesn't make it a good idea.
    • CurtainDog
      CurtainDog almost 12 years
      You 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
      Sheldon over 11 years
    • ChenZhou
      ChenZhou over 11 years
      As 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
      cellepo over 5 years
      Did you really intend to ask about Collection specifically, rather than List which you use in your code? If you intended to ask about List and not Collection, then please edit this question to reflect that - then this question would not be a duplicate! (One big difference of List vs Collection is that List includes get in its interface, while Collection does not).
    • Enginer
      Enginer almost 3 years
      You can use removeIf method. Example: sockets.removeIf(Socket::isClosed);
  • Varun Mehta
    Varun Mehta over 13 years
    Why add the overhead of another list ?
  • Chathuranga Withana
    Chathuranga Withana about 13 years
    Since the for-each loop hides the iterator, you cannot call the remove() directly. So in order to remove items from a for-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
    Mark McKenna over 12 years
    Just 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
    mmatloka over 12 years
    Its 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
    John Mellor about 12 years
    Note 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.
    James P. almost 12 years
    I'm curious, why is this considered safe? Is the Iterator acting as middle man?
  • Ethan Reesor
    Ethan Reesor almost 12 years
    I second James's question. What about that method is better?
  • Mark
    Mark almost 12 years
    To 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
    Admin almost 11 years
    but 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
    Admin almost 11 years
    this isn't calling remove() in a foreach loop
  • shareef
    shareef almost 11 years
    FYI you can call it.next() in if statment instead of the sample above -- or you can do as the code above
  • Jake Toronto
    Jake Toronto over 9 years
    Note: 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 ith element. If you were looping from 0 to size()-1, and doing the remove, you'd see the issues mentioned in other answers. Nice job, though!
  • committedandroider
    committedandroider about 9 years
    I 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
    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
    itzhar over 8 years
    this supposed to be the first answer here...
  • D3V
    D3V over 8 years
    Beware. For composite objects with no equals and hashCode method, it might just fail. However, marked answer safely removes it.
  • DavidR
    DavidR over 7 years
    This is just a horrible way to do. So many things can go wrong depending on the implementation of the List (or Set, etc.).
  • TheArchon
    TheArchon over 7 years
    Such 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
    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
    forresthopkinsa over 6 years
    As 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
    Serafeim over 6 years
    The 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
    SND over 6 years
    Though 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
    cellepo over 5 years
    Analogous code that iterates from beginning (increments i instead conditionally in body, which I believe alleviates most if not all other concerns).
  • cellepo
    cellepo over 5 years
    Either 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
    cellepo over 5 years
    Although it will avoid ConcurrentModificationException, there could still be other indexing problems/Exceptions (more details).
  • cellepo
    cellepo over 5 years
    Although CopyOnWriteArrayList will avoid ConcurrentModificationException, there could still be other indexing problems/Exceptions (more details).
  • FINDarkside
    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
    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
    pratpor over 4 years
    Thumbs up for the extra Note *that the code calls Iterator.remove, not List.remove". I almost missed and used list.remove
  • Serafins
    Serafins over 4 years
    as of Java 8 you can actually use: names.removeIf( t -> t <some condition>)