Issues with removing elements from a list in Java/Kotlin within a loop
This post will discuss the issues involved in removing elements from a list within a loop in Java and Kotlin.
It is tricky to add or remove elements from a list, within a loop, as the index of its elements and the length of the list is changed.
1. Incorrect output / IndexOutOfBoundsException
Consider the following example where we’re creating a list of colors and calling the filterList() method that is supposed to remove elements from the specified list that has a RED color.
Java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; class Main { public static <T> void filterList(List<T> list, Predicate<T> condition) { for (int i = 0; i < list.size(); i++) { if (condition.test(list.get(i))) { list.remove(i); } } } public static void main(String[] args) { List<String> colors = new ArrayList<>(); colors.addAll(Arrays.asList("YELLOW", "RED", "RED", "BLUE")); filterList(colors, s -> s.equals("RED")); System.out.println(colors); // Incorrect output: [YELLOW, RED, BLUE] } } |
Kotlin
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.util.function.Predicate fun <T> filterList(list: MutableList<T>, condition: Predicate<T>) { // might throw `java.lang.IndexOutOfBoundsException` for (i in list.indices) { if (condition.test(list[i])) { list.removeAt(i) } } } fun main() { val colors: MutableList<String> = mutableListOf("YELLOW", "RED", "RED", "BLUE") filterList(colors, Predicate { i: String -> i == "RED" }) println(colors) } |
There are two RED color instances in the original list. Still, the Java code will only remove the first instance of RED, and the Kotlin code will throw java.lang.IndexOutOfBoundsException.
The second RED is skipped because it is adjacent to the first RED. Once the first RED is removed, the list order is changed, and the second RED takes the place of the first RED, but the index of the list also gets incremented by 1, which now points to BLUE color.
In other words, when the i'th element of the list is removed, the element positioned at the next index becomes the new i'th element. Now in the next iteration of the for-loop, since index i gets incremented, the unprocessed i'th element will be skipped.
2. Using java.util.ConcurrentModificationException
It is not permissible to modify a list while iterating over it; otherwise, java.util.ConcurrentModificationException will be thrown to avoid non-deterministic behavior at a later stage.
For example, consider the following example which throws a java.util.ConcurrentModificationException as they permit concurrent modification:
Java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.function.Predicate; class Main { public static <T> void filterList(List<T> list, Predicate<T> condition) { Iterator<T> itr = list.iterator(); while (itr.hasNext()) { // throws `java.util.ConcurrentModificationException` T t = itr.next(); if (condition.test(t)) { list.remove(t); } } } public static void main(String[] args) { List<String> colors = new ArrayList<>(); colors.addAll(Arrays.asList("YELLOW", "RED", "RED", "BLUE")); filterList(colors, s -> s.equals("RED")); System.out.println(colors); } } |
Kotlin
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.util.function.Predicate fun <T> filterList(list: MutableList<T>, condition: Predicate<T>) { val itr: Iterator<T> = list.iterator() while (itr.hasNext()) { val t = itr.next() // throws `java.util.ConcurrentModificationException` if (condition.test(t)) { list.remove(t) } } } fun main() { val colors: MutableList<String> = mutableListOf("YELLOW", "RED", "RED", "BLUE") filterList(colors, Predicate { i: String -> i == "RED" }) println(colors) } |
Another plausible way of iteration is using the for-each loop (which internally uses an iterator):
Java
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; class Main { public static <T> void filterList(List<T> list, Predicate<T> condition) { // throws `java.util.ConcurrentModificationException` for (T t: list) { if (condition.test(t)) { list.remove(t); } } } public static void main(String[] args) { List<String> colors = new ArrayList<>(); colors.addAll(Arrays.asList("YELLOW", "RED", "RED", "BLUE")); filterList(colors, s -> s.equals("RED")); System.out.println(colors); } } |
Kotlin
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import java.util.function.Predicate fun <T> filterList(list: MutableList<T>, condition: Predicate<T>) { // throws `java.util.ConcurrentModificationException` for (t in list) { if (condition.test(t)) { list.remove(t) } } } fun main(args: Array<String>) { val colors: MutableList<String> = mutableListOf("YELLOW", "RED", "RED", "BLUE") filterList(colors, Predicate { i: String -> i == "RED" }) println(colors) } |
However, there are several workarounds to deal with this problem. These alternatives are discussed in detail in the following post:
That’s all about issues associated with removing elements from a list in Java or Kotlin within a loop.
Thanks for reading.
To share your code in the comments, please use our online compiler that supports C, C++, Java, Python, JavaScript, C#, PHP, and many more popular programming languages.
Like us? Refer us to your friends and support our growth. Happy coding :)