Which is better?: ``` .flow().map { ... }.toList()...
# coroutines
r
Which is better?:
Copy code
.flow().map { ... }.toList()
or
Copy code
.flow().toList().map { ... }
o
probably the former in most cases, the latter will build up a copy in memory
v
I believe the second will result in the creation of two separate ArrayLists, while the first one will perform the mapping directly on the elements emitted by the flow and then collect to a single ArrayList
l
First one should be more efficient, and the difference should be more and more significant as the number of values from the flow increases.
a
No need to speculate, this is easy to benchmark:
Copy code
kotlin
val f1_000 = flowOf(*(IntArray(1000) { it }.toTypedArray()))
val f10_000 = flowOf(*(IntArray(10000) { it }.toTypedArray()))
val f100_000 = flowOf(*(IntArray(100000) { it }.toTypedArray()))
val f1_000_000 = flowOf(*(IntArray(1000000) { it }.toTypedArray()))

open class Benchmarks {
    @Benchmark fun map_first_1_000() = runBlocking { f1_000.map { it }.toList() }
    @Benchmark fun map_first_10_000() = runBlocking { f10_000.map { it }.toList() }
    @Benchmark fun map_first_100_000() = runBlocking { f100_000.map { it }.toList() }
    @Benchmark fun map_first_1_000_000() = runBlocking { f1_000_000.map { it }.toList() }

    @Benchmark fun list_first_1_000() = runBlocking { f1_000.toList().map { it } }
    @Benchmark fun list_first_10_000() = runBlocking { f10_000.toList().map { it } }
    @Benchmark fun list_first_100_000() = runBlocking { f100_000.toList().map { it } }
    @Benchmark fun list_first_1_000_000() = runBlocking { f1_000_000.toList().map { it } }
}
Copy code
Benchmark                  Mode  Cnt         Score         Error  Units
Benchmarks.map_first_1_000      avgt    3      9644.894 ±     118.008  ns/op
Benchmarks.list_first_1_000     avgt    3     11846.879 ±    3183.500  ns/op
Benchmarks.map_first_10_000     avgt    3     95552.804 ±    9591.806  ns/op
Benchmarks.list_first_10_000    avgt    3    115079.001 ±    5788.557  ns/op
Benchmarks.map_first_100_000    avgt    3    931813.515 ±   47881.381  ns/op
Benchmarks.list_first_100_000   avgt    3   1124461.340 ±  119325.673  ns/op
Benchmarks.map_first_1_000_000  avgt    3  10754296.419 ±  429160.584  ns/op
Benchmarks.list_first_1_000_000 avgt    3  13008870.700 ± 1558059.298  ns/op
Using
flow.map{}.toList()
is indeed slightly faster than
flow.toList().map()
, but by only ~10ns/item. Ultimately, it doesn't matter which form you use, since the cost of doing the mapping operation and generating the flow will be orders of magnitude slower.
🔥 2
u
The penalty might come after the test with the next GC