Foreach vs. for (int ...) loop - why does foreach throw exceptions when deleting elements?

I was wondering why I cannot remove items from the list when I repeat it using the foreach loop:

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 remove items such as:

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

It seems that foreach-loop does not allow the removal of objects that "remain" in the loop queue. Am I right?

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

thanks

+4
source share
4 answers

Well, the question was originally tagged C #, now deleted. but....

It is not true that there is no for / for loop if you remove an item from the collection. If you delete items while moving forward, you will have problems indexing items after you delete, because the index no longer matches your next item. You avoid for / loop issues only if you loop in reverse order (from last element to first)

In foreach, the same problem internally arises. The enumerator supports the Current element for the element, if you delete the element and then move to the Next position, the Current indexer does not point to the element after deletion, but to the 2 element of the position after deletion.

For instance:

Suppose a set of strings of "birds", "animals" and "fish"

  • When starting foreach, the internal current is 0 (indicates "birds")
  • you remove the “birds”, so the “animals” are at index 0 (where the current still indicates)
  • You move on to the next element (Current = 1, indicates "fish")
  • Without exception, the subsequent transition from the cycle will end.

foreach was not designed to allow this scenario.

+3
source

As for Java, the answer is in javadoc (highlighted by me):

The iterators returned by this class iterator method and listIterator methods fail: if the list changes at any time after creating the iterator, in any way other than using its own methods to remove or add an iterator, the iterator will throw a ConcurrentModificationException. Thus, in the face of simultaneous modification, the iterator does not work quickly and cleanly, and does not risk arbitrary, non-deterministic behavior at an indefinite time in the future.

+2
source

See Deleting a call in a foreach loop in Java , generally use an Iterator and check with hasNext if there are more elements

+1
source

For Java, at least you are right: the problem is that the iterator used to traverse the items in the list does not allow changes. It throws a ConcurrentModificationException. To quote the javadoc Java API for java.util.ArrayList

The iterators returned by this class by the iterator and listIterator methods do not work quickly: if the list is structurally modified at any time after creating the iterator, in any way, except for the iterator itself deleting or adding methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of parallel modifications, the iterator fails quickly and cleanly, and not at the risk of arbitrary, non-deterministic behavior at an undetermined time in the future.

An alternative is to use an iterator that allows such modifications, rather than the default for an ArrayList, or to create an iterator explicitly, instead of using the foreach loop and using the methods that it should change, while iterating through the list.

+1
source

Source: https://habr.com/ru/post/1483050/


All Articles