5

I was wondering why I cannot remove elements from a list, when I'm iterating it with a foreach-loop like:

List<Object> objects = new ArrayList<Object>();
Object one = new Object();
Object two = new Object();
Object three = new Object(); 

objects.add(one);
objects.add(two);
objects.add(three);

and then removing the elements like:

foreach(Object o : objects){
  objects.remove(three); //I know that o is my current object
}

It seems like the foreach-loop doesn't allow to remove objects, which are "still" in the loop-queue. Am I correct?

And why does the for-int-loop doesn't care about this? In this loop I can easily remove objects, which are still in the loop.

Thanks

Ben Reich
  • 16,222
  • 2
  • 38
  • 59
augmentie123
  • 1,019
  • 1
  • 8
  • 10

4 Answers4

4

Well, initially the question was tagged C#, now removed. however....

It's not true that a for/loop doesn't care if you remove an element from the collection. If you remove elements while going forward, you will have problem indexing elements after the one you have removed because the index doesn't match anymore your next element. You avoid problems with for/loop only if you loop in reverse order (from the last element to the first)

The foreach suffers internally of the same problem. The enumerator maintains a Current position to the element, if you remove the element and then advance to the Next position, the Current indexer doesn't point to the element after the one removed, but to an element two position after the one removed.

For example:

Suppose a string collection of "birds", "animals", and "fishes"

  • At the foreach start, the internal Current is 0 ( points to "birds")
  • you remove "birds", so "animals" is at index 0 (where Current still points to)
  • You advance to the Next element (Current=1, points to "fishes")
  • Without the exception the Next advance will exit the loop.

foreach has not been designed to allow this scenario.

Steve
  • 213,761
  • 22
  • 232
  • 286
2

As far as Java is concerned, the answer is in the javadoc (emphasis mine):

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.

assylias
  • 321,522
  • 82
  • 660
  • 783
1

See Calling remove in foreach loop in Java, in general use Iterator and check with hasNext if there are more elements

Community
  • 1
  • 1
Frame91
  • 3,670
  • 8
  • 45
  • 89
1

For Java at least, you are correct: the problem is the iterator used to go through the elements in the list does not allow modification. It will throw a ConcurrentModificationException. To quote the Java API javadoc for java.util.ArrayList

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.

The alternative is to use an iterator that allows such modifications, rather than the default for ArrayList or to create the iterator explicitly, rather than use the foreach loop, and use the methods it has to alter the contents whilst iterating through the list.

Joshua Hutchison
  • 567
  • 8
  • 17