When you are iterating through a list it often happens that you also want to remove some elements in the process. For example, you have a list of elements, and you only want to keep some of them, that match certain criterias. This seems like a very easy task to do, but it has some pitfalls that is not so obvious to avoid.
Let’s create a list of 5 integers, that we will use throughout our examples.
List<Integer> elementList = new ArrayList<>();
elementList.add(10);
elementList.add(20);
elementList.add(30);
elementList.add(40);
elementList.add(50);
Our task is to remove all the numbers that are either greater than 45 or less than 25. You can easily see, that this should leave us with a list that contains only 30 and 40.
Try to remove element with foreach
One of the obvious options is to go through the list with a foreach loop and just remove the elements that you don’t need. Like this:
for (Integer element : elementList) {
if((element > 45) || (element < 25)) {
elementList.remove(element);
}
}
You might think that this should work. But it does not. It will throw you an exception similar to this:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at example.ExampleMain.main(ExampleMain.java:18)
This is because you cannot remove elements from a list while iterating over it in a foreach loop, as it could cause some unexpected behaviour, so to be on the safe side, this is simply not allowed. But there are ways to overcome this limitation.
Working example: Iterator
Using an iterator you are able to remove elements on the fly, while iterating.
Iterator<Integer> elementListIterator = elementList.iterator();
while (elementListIterator.hasNext()) {
Integer element = elementListIterator.next();
if((element > 45) || (element < 25)) {
elementListIterator.remove();
}
}
This works, because the iterator acts as a middle layer to make sure that iterating can continue even if the underlying collection changes.
And to give a more concrete explanation of the failure of our previous method, here is a snippet from the documentation of the 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.
So only the iterator’s own methods could be used, but in the first example we modified the list by using the list’s own remove method. This was what caused the problem.
Working example: Java 8 lamba expression
In Java 8, the method removeIf
has been introduced for collections. It allows you to remove all elements from the collection that satisfy the given predicate. It is nice and short, only takes one line:
elementList.removeIf(element -> (element > 45) || (element < 25));
That’s it. One line, no frills, it just works and easily understandable.