Calling Remove in Foreach Loop in Java

Calling remove in foreach loop in Java

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

Removing object from ArrayList in for each loop

You can't, within the enhanced for loop. You have to use the "long-hand" approach:

for (Iterator<Pixel> iterator = pixels.iterator(); iterator.hasNext(); ) {
Pixel px = iterator.next();
if(px.y > gHeigh){
iterator.remove();
}
}

Of course, not all iterators support removal, but you should be fine with ArrayList.

An alternative is to build an additional collection of "pixels to remove" then call removeAll on the list at the end.

Remove elements from collection while iterating

Let me give a few examples with some alternatives to avoid a ConcurrentModificationException.

Suppose we have the following collection of books

List<Book> books = new ArrayList<Book>();
books.add(new Book(new ISBN("0-201-63361-2")));
books.add(new Book(new ISBN("0-201-63361-3")));
books.add(new Book(new ISBN("0-201-63361-4")));

Collect and Remove

The first technique consists in collecting all the objects that we want to delete (e.g. using an enhanced for loop) and after we finish iterating, we remove all found objects.

ISBN isbn = new ISBN("0-201-63361-2");
List<Book> found = new ArrayList<Book>();
for(Book book : books){
if(book.getIsbn().equals(isbn)){
found.add(book);
}
}
books.removeAll(found);

This is supposing that the operation you want to do is "delete".

If you want to "add" this approach would also work, but I would assume you would iterate over a different collection to determine what elements you want to add to a second collection and then issue an addAll method at the end.

Using ListIterator

If you are working with lists, another technique consists in using a ListIterator which has support for removal and addition of items during the iteration itself.

ListIterator<Book> iter = books.listIterator();
while(iter.hasNext()){
if(iter.next().getIsbn().equals(isbn)){
iter.remove();
}
}

Again, I used the "remove" method in the example above which is what your question seemed to imply, but you may also use its add method to add new elements during iteration.

Using JDK >= 8

For those working with Java 8 or superior versions, there are a couple of other techniques you could use to take advantage of it.

You could use the new removeIf method in the Collection base class:

ISBN other = new ISBN("0-201-63361-2");
books.removeIf(b -> b.getIsbn().equals(other));

Or use the new stream API:

ISBN other = new ISBN("0-201-63361-2");
List<Book> filtered = books.stream()
.filter(b -> b.getIsbn().equals(other))
.collect(Collectors.toList());

In this last case, to filter elements out of a collection, you reassign the original reference to the filtered collection (i.e. books = filtered) or used the filtered collection to removeAll the found elements from the original collection (i.e. books.removeAll(filtered)).

Use Sublist or Subset

There are other alternatives as well. If the list is sorted, and you want to remove consecutive elements you can create a sublist and then clear it:

books.subList(0,5).clear();

Since the sublist is backed by the original list this would be an efficient way of removing this subcollection of elements.

Something similar could be achieved with sorted sets using NavigableSet.subSet method, or any of the slicing methods offered there.

Considerations:

What method you use might depend on what you are intending to do

  • The collect and removeAl technique works with any Collection (Collection, List, Set, etc).
  • The ListIterator technique obviously only works with lists, provided that their given ListIterator implementation offers support for add and remove operations.
  • The Iterator approach would work with any type of collection, but it only supports remove operations.
  • With the ListIterator/Iterator approach the obvious advantage is not having to copy anything since we remove as we iterate. So, this is very efficient.
  • The JDK 8 streams example don't actually removed anything, but looked for the desired elements, and then we replaced the original collection reference with the new one, and let the old one be garbage collected. So, we iterate only once over the collection and that would be efficient.
  • In the collect and removeAll approach the disadvantage is that we have to iterate twice. First we iterate in the foor-loop looking for an object that matches our removal criteria, and once we have found it, we ask to remove it from the original collection, which would imply a second iteration work to look for this item in order to remove it.
  • I think it is worth mentioning that the remove method of the Iterator interface is marked as "optional" in Javadocs, which means that there could be Iterator implementations that throw UnsupportedOperationException if we invoke the remove method. As such, I'd say this approach is less safe than others if we cannot guarantee the iterator support for removal of elements.

how to remove object from stream in foreach method?

Streams are designed to be used in a more functional way, preferably treating your collections as immutable.

The non-streams way would be:

arrB.addAll(arrA);
arrA.clear();

However you might be using Streams so you can filter the input so it's more like:

arrB.addAll(arrA.stream().filter(x -> whatever).toList())

then remove from arrA (thanks to @Holgar for the comment).

arrA.removeIf(x -> whatever)

If your predicate is expensive, then you could partition:

Map<Boolean, XXX> lists = arrA.stream()
.collect(Collectors.partitioningBy(x -> whatever));
arrA = lists.get(false);
arrB = lists.get(true);

or make a list of the changes:

List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);

Java list's .remove method works only for second last object inside for each loop

In a list, adding or removing is considered as a modification. In your case you have made
5 modifications(additions).

‘for each’ loop works as follows,

1.It gets the iterator.
2.Checks for hasNext().
public boolean hasNext() 
{
return cursor != size(); // cursor is zero initially.
}

3.If true, gets the next element using next().

public E next() 
{
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}

final void checkForComodification()
{
// Initially modCount = expectedModCount (our case 5)
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

Repeats steps 2 and 3 till hasNext() returns false.

In case if we remove an element from the list , it’s size gets reduced and modCount is increased.

If we remove an element while iterating, modCount != expectedModCount get satisfied
and ConcurrentModificationException is thrown.

But removal of second last object is weird. Lets see how it works in your case.

Initially,

cursor = 0 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.

cursor = 1 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.

cursor = 2 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.

cursor = 3 size = 5 --> hasNext() succeeds and next() also succeeds
without exception.

In your case as you remove ‘d’ , size gets reduced to 4.

cursor = 4 size = 4 --> hasNext() does not succeed and next() is
skipped.

In other cases, ConcurrentModificationException will be thrown as modCount != expectedModCount.

In this case, this check does not take place.

If you try to print your element while iterating, only four entries will be printed. Last element is skipped.

Hope I made clear.

remove element from ArrayList inside for(Particle p: particles) loop

You can't do that because you'll get a ConcurrentModificationException. ConcurrentModificationException is thrown when something you're iterating over is modified.

You need to create an iterator and use that to remove your elements. Here's an example:

Iterator iterator = particles.iterator();
while(iterator.hasNext()) {
iterator.remove();
iterator.next();
}

removing object from arraylist using foreach exception occurs only when the list size above 2

To answer why it doesn't throw an error on 2 elements you have to look at what the for each loop actually does.for(Type something:myList) compiles to something like:

Iterator<Type> iter = myList.iterator();
while(iter.hasNext()){
Type something= iter.next()
//your code
}

Now look at the source code for ArrayList for the actual implementation. The code for hasNext() is:

return cursor != size; //cursor is the current position in the list

So when you remove the element in the loop, the size is now 1. When it looks to see if there are more elements it will return false because it thinks that is at the end of the ArrayList, and stops executing the loop at this point.

Now look at the code for next():

public E next() {
checkForComodification();
//more code
}

This is where the actual checking for modification occurs, so when size is 3, it will execute the second iteration and when it calls next() to get the second element, it will thrown an exception because you have modified the ArrayList outside of the iterator.

Foreach against for(int...) loop - why does foreach raises exceptions when removing elements?

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.



Related Topics



Leave a reply



Submit