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?
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
iterator()
on anything unless you know you need one for some reason, like you're going to be calling hasNext()
on it or whateverforEach
fits with Kotlin's more declarative style, can be chained, and automatically passes in variables instead of you having to declare themfor
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 anywayentries
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