There are several ways to do this. Let's look at the alternatives:
Iterating over a copy, removing from original
This is a simple solution for the underlying problem of your first code: A ConcurrentModificationException
is thrown because you iterate through the list and removing from it at the same time.
Easy solution is to create a copy of the list and iterate through that.
for (Integer integer : new ArrayList<>(nums)) {
if (integer < 3) {
nums.remove(integer);
}
}
Down-sides of this approach:
- Creates a copy of the original list, which requires memory and an operation which performance depends on the type of the list (ArrayList, LinkedList, etc.)
- Additionally,
nums.remove(value)
is a \$O(n)\$ operation. Making this loop overall \$O(n^2)\$
Java 8 Streams
List<Integer> filteredList = nums.stream().filter(i -> i >= 3).collect(Collectors.toList());
Down-sides:
- Does not actually modify the existing list, so if references to the list are spread around various variables, you still have some old elements that just shouldn't be in that list.
- Creates various stream-related objects which might not be the most effective option.
On the up-side, this is among the fastest for bigger lists.
If you're not using Java 8:
List<Object> originalList;
List<Object> newList = new YourFavoriteListType<>();
for (Object obj : originalList) {
if (shouldKeep(obj)) {
newList.add(obj);
}
}
Java 8 method
nums.removeIf(i -> i < 3);
Java 8 introduced the default method removeIf
on the Collection
interface. This allows different implementations to have implementation-specific performance-optimized implementations of this method.
Iterator.remove()
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
Integer integer = it.next();
if (integer < 3) {
it.remove();
}
}
The only down-side of this approach is that you need to switch your for-each to a while
. However, this approach is the most efficient one, especially for LinkedList
where it is \$O(n)\$ (it's \$O(n^2)\$ for ArrayList
because it has to copy array data on each remove(index)
call). This is the approach I would recommend in most cases.
Note: Instead of using a while-loop it can also be written as:
for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
Integer integer = it.next();
...
Conclusion
If you want to mutate the existing list, removeIf
is the solution I would go with. If you like functional programming and prefer a new list instead of mutating the existing one, then go with the list.stream().filter(...).collect(Collectors.toList())
approach.
See also
"When to use LinkedList over ArrayList?" on Stack Overflow