Home > OS >  How to iterate over a Map in Kotlin
How to iterate over a Map in Kotlin

Time:07-03

So I am new to Kotlin and I am wondering what's the standard way of iterating a Map. I have tried different ways and all of them seem to work, but I don't know if there's one better than the rest or there are some differences that I am not aware of.

    var mutMap = mutableMapOf("one" to 1, "two" to 2, "tree" to 3, "four" to 4, "five" to 5)

    mutMap.forEach { entry -> println(entry) }

    mutMap.iterator().forEach { entry -> println(entry) }

    mutMap.entries.forEach { entry -> println(entry) }

    mutMap.entries.iterator().forEach { entry -> println(entry) }

    for (entry in mutMap) { println(entry) }

    for (entry in mutMap.entries) { println(entry) }

    for (entry in mutMap.iterator()) { println(entry) }

    for (entry in mutMap.entries.iterator()) { println(entry) }

Also, if I wanted to also delete an entry while iterating over them, none of them would work, right?

CodePudding user response:

You can use the remove() function of the iterator to remove the item of map.

fun main() {
    val map = mutableMapOf("user1" to 29, "user2" to 25, "user3" to 26)
    val iterator = map.entries.iterator()
    println("Before iteration: $map")
    while (iterator.hasNext()) {
        val (key, value) = iterator.next()
        if (key == "user1") {
            iterator.remove()
        }

    }
    println("After iteration: $map")
}

It'll prints:

Before iteration: {user1=29, user2=25, user3=26}
After iteration: {user2=25, user3=26}

CodePudding user response:

If you browse through Kotlin's Collections package there is a whoooole lot of stuff you can use, yeah! Lots of different functions that let you drill down into specific pieces of data (like keys or values vs entries, or providing indices) or getting specific behaviour as you process a collection.

The examples you've given are all basically the same thing though. Here's the page for all the forEach functions on the various types of collections:

inline fun <T> Array<out T>.forEach(action: (T) -> Unit)
(source)

// a bunch of other Kotlin-specific Array types

inline fun <T> Iterable<T>.forEach(action: (T) -> Unit)
inline fun <K, V> Map<out K, V>.forEach(
    action: (Entry<K, V>) -> Unit)
inline fun <T> Iterator<T>.forEach(operation: (T) -> Unit)

And here's the source code for those (there's a source link under every function's main page, useful to know about! You can see exactly how they work)

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

public inline fun <K, V> Map<out K, V>.forEach(action: (Map.Entry<K, V>) -> Unit): Unit {
    for (element in this) action(element)
}

public inline fun <T> Iterator<T>.forEach(operation: (T) -> Unit): Unit {
    for (element in this) operation(element)
}

So really they're all wrappers for a basic for loop, which as the documentation says, iterates through anything that provides an iterator. Your examples are all basically the same thing that's happening, just jumping in at various points in the forEach -> basic for loop -> get an iterator process.

The only part that's different is when you call entries, which returns a Set holding the key/value Entry pairs - so you're iterating over that, rather than the Map itself. But wait, what does happen if you call iterator() on a Map?

public inline operator fun <K, V> Map<out K, V>.iterator(): Iterator<Map.Entry<K, V>> = entries.iterator()

It uses entries itself! So yeah they're all the same thing


Really I think it comes down to this

  • no need to call iterator() on anything unless you know you need one for some reason, like you're going to be calling hasNext() on it or whatever
  • forEach fits with Kotlin's more declarative style, can be chained, and automatically passes in variables instead of you having to declare them
  • sometimes a basic for loop is more readable for what you're doing though, especially if you're modifying some kind of result variable (which is more comparable to a fold than a forEach but anyway
  • accessing entries on a Map means you're being explicit about what you're working with when you chain a forEach onto it, similar to how you can specify keys or values and only work with those. It's the same as just calling forEach on the map itself, but it's clearer
  • Related