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
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
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}
Related
Example:
sealed interface Foo<out T> {
val value: T
}
data class Bar<out K: List<Int>>(override val value: K): Foo<K>
fun <T> processFoo(foo: Foo<T>) {
when (foo) {
is Bar -> foo.value.forEach(::println)
}
}
Fails with:
Unresolved reference. None of the following candidates is applicable
because of receiver type mismatch:
public inline fun <T> Iterable<TypeVariable(T)>.forEach(action: (TypeVariable(T)) -> Unit): Unit defined in kotlin.collections
public inline fun <K, V> Map<out TypeVariable(K), TypeVariable(V)>.forEach(action: (Map.Entry<TypeVariable(K), TypeVariable(V)>) -> Unit): Unit defined in kotlin.collections
Why this fails? I expect that if foo is of type Bar then we know that T is a subtype of List<Int>. So we should be able to call forEach on it. Am I wrong?
This problem is simply caused by a typo in your code.
If you replace is Bar with is Bar<*>, the compiler is able to infer that T is a List<Int> in that context and the code compiles.
I expect that if foo is of type Bar then we know that T is a subtype of List. So we should be able to call forEach on it.
Yes, that is true. But T could also implement Map<K, V> at the same time as it implements List<Int> (I don't know of such a type, but it theoretically could exist), in which case you would also be able to call this extension function:
inline fun <K, V> Map<out K, V>.forEach(
action: (Entry<K, V>) -> Unit)
See all the different forEaches here.
To specifically call the forEach defined for Iterable, just do a cast:
// could also cast to List<Int> here - that's a completely safe unchecked cast
(foo.value as List<*>).forEach(::println)
An alternative is to use is Bar<*>, but a (very) slight drawback of this is that, as <*> projects the type of foo.value to be List<Int>, you lose the T. You won't be able to use foo.value in places where a T is expected.
A contrived example would be:
fun <T> processFoo(foo: Foo<T>): T {
return when (foo) {
// you can't return foo.value when you are expected to return T
is Bar<*> -> foo.value.also {
it.forEach(::println)
}
}
}
The goal is to store a list of generic objects (of different type argument) in a list and operate on them in a type safe and ergonomic way.
My current design uses a visitor pattern:
sealed interface Element {
fun visit(visitor: Visitor)
}
interface Visitor {
fun <T> accept(elementA: ElementA<T>)
}
data class ElementA<T>(
// example members, many more in the real code
val produce : () -> T,
val doSomething: (T) -> Unit
) : Element {
override fun visit(visitor: Visitor) = visitor.accept(this)
}
Nevertheless the visitor is not very convenient to write as one has to subclass Visitor:
fun exampleUse(elements: List<Element>) {
for (element in elements) {
element.visit(object : Visitor {
override fun <T> accept(elementA: ElementA<T>) {
// We don't care about the actual type T, just that it exists
elementA.doSomething(elementA.produce())
}
})
}
}
It's not very ergonomic, and I would like for users to only have to write conventional and short code like
element.visit {it.doSomething(it.produce())}.
The only requirements are that:
the elements should be storable in a list (homogeneous collection)
the usage (eg: a.doSomething(a.produce())) should kept be separated from the class as they are defined in different package with different concerns.
If there is a way to avoid the visitor boilerplate, it's even better.
This seems to satisfy your requirements (I am assuming Element exists only as a common supertype for all ElementA<Something>):
data class ElementA<T>(
// example members, many more in the real code
val produce : () -> T,
val doSomething: (T) -> Unit
)
// optional
typealias Element = ElementA<*>
// or fun exampleUse(elements: List<ElementA<*>>)
// if you don't create Element
fun exampleUse(elements: List<Element>) {
fun <T> use(elementA: ElementA<T>) {
// We don't care about the actual type T, just that it exists
elementA.doSomething(elementA.produce())
}
for (element in elements) {
use(element)
}
}
Note that the local function can also be an extension function:
fun exampleUse(elements: List<Element>) {
fun <T> ElementA<T>.use() {
// We don't care about the actual type T, just that it exists
doSomething(produce())
}
for (element in elements) {
element.use()
}
}
I'm not sure if this meets your requirements, but I implemented a type-safe heterogenous map in Kotlin: https://github.com/broo2s/typedmap . It is a little different than your example, because it is not a list, but a map. You can use it for example like this:
val map = simpleTypedMap()
sess += User("alice")
val user = sess.get<User>()
You can still store multiple items per a key type, but as this is a map, each item has to be uniquely identifiable, so I'm not sure if this suits you:
map[UserKey(1)] = User("alice")
map[UserKey(2)] = User("bob")
val alice = map[UserKey(1)]
val bob = map[UserKey(2)]
I am trying to switch from RxJava to Kotlin Flow. Flow is really impressive. But Is there any operator similar to RxJava's "GroupBy" in kotlin Flow right now?
As of Kotlin Coroutines 1.3, the standard library doesn't seem to provide this operator. However, since the design of Flow is such that all operators are extension functions, there is no fundamental distinction between the standard library providing it and you writing your own.
With that in mind, here are some of my ideas on how to approach it.
1. Collect Each Group to a List
If you just need a list of all items for each key, use this simple implementation that emits pairs of (K, List<T>):
fun <T, K> Flow<T>.groupToList(getKey: (T) -> K): Flow<Pair<K, List<T>>> = flow {
val storage = mutableMapOf<K, MutableList<T>>()
collect { t -> storage.getOrPut(getKey(t)) { mutableListOf() } += t }
storage.forEach { (k, ts) -> emit(k to ts) }
}
For this example:
suspend fun main() {
val input = 1..10
input.asFlow()
.groupToList { it % 2 }
.collect { println(it) }
}
it prints
(1, [1, 3, 5, 7, 9])
(0, [2, 4, 6, 8, 10])
2.a Emit a Flow for Each Group
If you need the full RxJava semantics where you transform the input flow into many output flows (one per distinct key), things get more involved.
Whenever you see a new key in the input, you must emit a new inner flow to the downstream and then, asynchronously, keep pushing more data into it whenever you encounter the same key again.
Here's an implementation that does this:
fun <T, K> Flow<T>.groupBy(getKey: (T) -> K): Flow<Pair<K, Flow<T>>> = flow {
val storage = mutableMapOf<K, SendChannel<T>>()
try {
collect { t ->
val key = getKey(t)
storage.getOrPut(key) {
Channel<T>(32).also { emit(key to it.consumeAsFlow()) }
}.send(t)
}
} finally {
storage.values.forEach { chan -> chan.close() }
}
}
It sets up a Channel for each key and exposes the channel to the downstream as a flow.
2.b Concurrently Collect and Reduce Grouped Flows
Since groupBy keeps emitting the data to the inner flows after emitting the flows themselves to the downstream, you have to be very careful with how you collect them.
You must collect all the inner flows concurrently, with no upper limit on the level of concurrency. Otherwise the channels of the flows that are queued for later collection will eventually block the sender and you'll end up with a deadlock.
Here is a function that does this properly:
fun <T, K, R> Flow<Pair<K, Flow<T>>>.reducePerKey(
reduce: suspend Flow<T>.() -> R
): Flow<Pair<K, R>> = flow {
coroutineScope {
this#reducePerKey
.map { (key, flow) -> key to async { flow.reduce() } }
.toList()
.forEach { (key, deferred) -> emit(key to deferred.await()) }
}
}
The map stage launches a coroutine for each inner flow it receives. The coroutine reduces it to the final result.
toList() is a terminal operation that collects the entire upstream flow, launching all the async coroutines in the process. The coroutines start consuming the inner flows even while we're still collecting the main flow. This is essential to prevent a deadlock.
Finally, after all the coroutines have been launched, we start a forEach loop that waits for and emits the final results as they become available.
You can implement almost the same behavior in terms of flatMapMerge:
fun <T, K, R> Flow<Pair<K, Flow<T>>>.reducePerKey(
reduce: suspend Flow<T>.() -> R
): Flow<Pair<K, R>> = flatMapMerge(Int.MAX_VALUE) { (key, flow) ->
flow { emit(key to flow.reduce()) }
}
The difference is in the ordering: whereas the first implementation respects the order of appearance of keys in the input, this one doesn't. Both perform similarly.
3. Example
This example groups and sums 40 million integers:
suspend fun main() {
val input = 1..40_000_000
input.asFlow()
.groupBy { it % 100 }
.reducePerKey { sum { it.toLong() } }
.collect { println(it) }
}
suspend fun <T> Flow<T>.sum(toLong: suspend (T) -> Long): Long {
var sum = 0L
collect { sum += toLong(it) }
return sum
}
I can successfully run this with -Xmx64m. On my 4-core laptop I'm getting about 4 million items per second.
It is simple to redefine the first solution in terms of the new one like this:
fun <T, K> Flow<T>.groupToList(getKey: (T) -> K): Flow<Pair<K, List<T>>> =
groupBy(getKey).reducePerKey { toList() }
Not yet but you can have a look at this library https://github.com/akarnokd/kotlin-flow-extensions .
In my project, I was able to achieve this non-blocking by using Flux.groupBy.
https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html#groupBy-java.util.function.Function-
I did this in the process of converting the results obtained with Flux to Flow.
This may be an inappropriate answer for the situation in question, but I share it as an example.
I know the convention is to use ?.let for null checking mutable variables because ?.let will make sure that the variable we're checking doesn't change to null in the middle of the block that's being executed. Will the same hold true for ?.apply and ?.run?
One last thing, if the variable is immutable is it recommended to just use a simple if?
Is there any difference in null checking quality between ?.apply, ?.run and ?.let in Kotlin?
Yes, they're all essentially the same when it comes to null checking quality. In fact, if you open the code for the apply, let, with, also & run. They're 'nearly' identical, they mainly differ on how the block get's executed, what argument is passed to the block and what value is returned.
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
They're really just syntactic sugar, that said it'd be a good idea for you to follow some basic rules/conventions on when to use what. Take a look at article I & article II, they explain the difference between them in much greater detail than I can elaborate in this answer, along with basic conventions on when to use what.
if the variable is immutable is it recommended to just use a simple if?
Yes, in fact, if you make an if check on a val variable, then the compiler will automatically understand that the variable will never be null inside the if block.
val user: User? = null;
if (user != null) {
// user not null
val name = user.name // won't show any errors
}
var user: User? = null;
if (user != null) {
// user might be null
// Since the value can be changed at any point inside the if block (or from another thread).
val name = user.name // will show an error
}
I'm trying to do something like this in a long chain of "stream" operations.
fun main(args: Array<String>) {
"test1, test2, test3".split(", ")
.toCustomString(StringBuilder(), StringBuilder::append)
}
fun <T, R>Iterable<T>.toCustomString(obj: R, thing: R.(T) -> Unit): R {
this.forEach {
obj.thing(it)
}
return obj
}
But this doesn't work it says none of the functions found for StringBuilder::append can't be applied here. Is there a way I can make something like this work?
You are trying to use a method reference with a different signature for a receiver function. You can make it work with supplying a lambda instead. Or as other answers point out, changing the signature of your receiver function.
fun main(args: Array<String>) {
"test1, test2, test3".split(", ")
.toCustomString(StringBuilder(), { item -> append(item) })
}
There's no problem to use a method reference in that case and it should work perfectly.
Just ensure you use kotlin class StringBuilder an change this:
fun <T, R>Iterable<T>.toCustomString(obj: R, thing: R.(T) -> Unit)
by this one:
fun <T, R>Iterable<T>.toCustomString(obj: R, thing: R.(T) -> R)
In order to use StringBuilder::append as a function reference, thing should have type R.(T) -> R instead of R.(T) -> Unit because StringBuilder.append(String) will return StringBuilder. However, since toCustomString does not handle the return value from StringBuilder.append, what #Marko suggested is more appropriate.
FYR, here is an alternative way to do it without an extra extension function:
val customString = StringBuilder().apply {
"test1, test2, test3"
.split(", ")
.forEach { this.append(it) }
}