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 givenListIterator
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 beIterator
implementations that throwUnsupportedOperationException
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
Why Is the Java Main Method Static
Appending to an Objectoutputstream
How to Run a Java Program from the Command Line on Windows
How to Sanity Check a Date in Java
When to Use: Java 8+ Interface Default Method, Vs. Abstract Method
What Is the Ellipsis (...) For in This Method Signature
Making a Robust, Resizable Swing Chess Gui
How to Build Jars from Intellij Properly
How to Remove Objects from an Array in Java
Why Does Java'S Hashcode() in String Use 31 as a Multiplier
Java: Unresolved Compilation Problem
When Does Static Class Initialization Happen
Insert & Fetch Java.Time.Localdate Objects To/From an SQL Database Such as H2