In rust iterators cost nothing. How kotlin compare...
# getting-started
v
In rust iterators cost nothing. How kotlin compares to this? Let's say i have an array of floats and i want to do something on them, like multiply each item by 2. And sum the results. Using simple for loop in kotlin would be faster then say: array.map(it -> it*2).sum() ?
e
Kotlin re-uses Java's collections which has a few effects: • iterators go through virtual dispatch • primitives are always boxed when placed into generic containers which means that
Copy code
array.map { it * 2 }.sum()
will perform worse than
Copy code
array.sumOf { it * 2 }
will perform like
Copy code
var sum = 0
for (int in array) sum += int * 2
however, Kotlin's approach also adds very little to the size of your library/application and remains easily compatible with Java (unlike Scala which has its own independent implementation of collections). it's a pragmatic trade-off
👍 3
v
Thanks. 👍
j
@ephemient why exactly would
sumOf
perform worse than the
for..in
loop? I thought it was inlined to do exactly the same thing. Doesn't
for..in
use iterators behind the scenes? If yes, I guess the "fastest" would be to avoid iterators completely by using a for loop on indices and directly accessing the primitives in the array, right? But maybe the compiler optimizes the iterator away, in which case I understand
e
you're right, I was thinking of
sumOf
as being generic, but it actually has distinct overloads for different primitive output types
k
@Joffrey :
But maybe the compiler optimizes the iterator away, in which case I understand (edited)
Indeed, it does. From https://kotlinlang.org/docs/control-flow.html#for-loops:
A
for
loop over a range or an array is compiled to an index-based loop that does not create an iterator object.
👍 2
🙏 1
e
along those lines though,
for
over a
List
compiles to an iterator-based loop, but for common list implementations, iterating over indices and using
get
performs better
I still write
for (item in list) { ... }
though, and not
for (index in list.indices) { val item = list[index]; ... }
, because the readability matters more
j
for over a List compiles to an iterator-based loop, but for common list implementations, iterating over indices and using get performs better
Yeah that's exactly why I was asking this for the case of arrays, because I've been bitten by this for lists once.
readability matters more
Yeah I actually almost never use loops at all, because usually extension functions express the intent better. But if I really have to, I'll use the
for in
with iterator despite the perf issue, it's so much more readable. I think the profiler only highlighted the perf problem once for me, for a specific use case of Minecraft, processing tons of 3D blocks. In other cases it was always something else
k
If you have a performance problem on the JVM with a
List
that gets initialized at the start and then never gets modified again, you should just create an array from that list (by calling
toArray()
) and use the array from then on. This is because the overhead, with both iterator and indexed access, is due to extra checks. Iterator access checks for co-modifications (which are unnecessary if there are no modifications), and Java's
ArrayList.get()
checks for out-of-bounds access, even though it doesn't need to because array indexing also does this check (I presume ArrayList does an additional check so that the exception message doesn't mention internal implementation details).
j
In my specific case, many such lists were created dynamically IIRC, so I wouldn't have been able to do such optimization. But those are good points, thanks!