Calling remove in foreach loop in Java

This question already has an answer here:

  • Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop 21 answers

  • 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


    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 .


    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.

    链接地址: http://www.djcxy.com/p/8836.html

    上一篇: 并行使用的管道模式ForEach C#

    下一篇: 在Java中调用foreach循环中的remove