1. Overview

Kotlin supports both immutable and mutable maps. However, by definition, removing an entry is only supported for mutable maps. Moreover, for an immutable map, we’ll get a compilation error while accessing the remove() method.

In this tutorial, we’ll learn how to remove a key from a mutable map in Kotlin while iterating over it.

2. Understanding the Scenario

In this section, we’ll understand the problem statement in detail. Further, we’ll try to solve the problem using a naive approach and identify the bug in it.

2.1. Problem Statement

Let’s define a mutableMap variable that contains a mapping between pairs of Int values:

val map = mutableMapOf(1 to 3, 2 to 3, 3 to 2, 4 to 2, 7 to 2, 9 to 1)

We have six entries in our map: two with even-numbered keys and four with odd-numbered keys.

Our goal is to remove all the entries with even-numbered keys from the map. So, we expect to remove the two entries with even-number keys, leaving behind four odd ones.

2.2. Naive Approach Throws ConcurrentModificationException

To remove the entries, we’re going to iterate over the map using a for loop:

for ((key, value) in map) {
    // delete logic
}

Unfortunately, on adding the delete logic, we encounter a ConcurrentModificationException with this approach:

assertFailsWith<ConcurrentModificationException> {
    for ((key, value) in map) {
        if (key % 2 == 0) {
            map.remove(key)
        }
    }
}

It’s illegal to remove an entry from a map while iterating over it using a for loop. By refusing to remove an entry from the map while iterating over it, Kotlin prevents unintended side effects and makes our program more predictable.

In the following sections, let’s explore a few alternate techniques to work around this limitation.

3. Using Mark and Remove

With this technique, we’ll first iterate over the map and identify the keys for removal. Later, we’ll use this secondary data structure to remove the keys directly without iterating over the original map.

First, let’s define a set of candidates to hold the candidate keys that we’ll eventually remove from the map:

val candidates = HashSet<Int>()

Next, in our first phase of iteration, we’ll mark the candidate keys by adding them to the candidates set:

for ((key, value) in map) {
    if (key % 2 == 0) {
        candidates.add(key)
    }
}

Furthermore, the second phase involves iterating over the candidates set and removing entries from the map using their keys:

for (candidate in candidates) {
    map.remove(candidate)
}

We used the remove() method of map to delete map entries using their keys.

Finally, let’s verify that the removal operations were successful:

assertEquals(4, map.size)

Great! Our approach worked fine.

4. Using an Iterator

The remove() method of an Iterator is specifically designed to allow the safe removal of items while iterating over them. So, let’s explore how we can use this approach to solve our use case.

First, let’s obtain an iterator for the entries property of map:

val iterator = map.entries.iterator()

Next, we can use the iterator.remove() method to remove an entry while stepping through the entries:

while (iterator.hasNext()) {
    val entry = iterator.next()
    if (entry.key % 2 == 0) {
        iterator.remove()
    }
}

Lastly, let’s confirm that it gives the desired result:

assertEquals(4, map.size)

Perfect! We got this one right.

5. Using a Predicate

In our Iterator-based approach, we used a while loop to iterate over the map entries explicitly. However, we can instead use the higher-order function removeIf to delegate the iteration logic to Kotlin while focusing only on the delete logic.

Let’s use a lambda expression to define a Predicate and pass it to the removeIf method of the entries property of map:

map.entries.removeIf { it.key % 2 == 0 }

Internally, the removeIf method uses an Iterator and invokes the remove() method to delete entries whose key matches the given Predicate.

Further, let’s confirm that the deletions were successful:

assertEquals(4, map.size)

Fantastic! It looks like we nailed this one.

6. Conclusion

In this tutorial, we noticed that removing an entry from a map while iterating using a for loop throws a ConcurrentModificationException exception.

To address this issue, we learned how to remove entries safely using techniques such as the remove() method of an Iterator or using a Predicate with the removeIf() method.

As always, the code from this article is available over on GitHub.

Comments are closed on this article!