While using Jetpack Compose, I noticed a fastForEach
extension defined for some random–access collections to avoid the overhead of creating a new iterator.
At this point, I wonder if a change could be made to the Kotlin language so that tricks like those are not needed.
If the forEach
extension method that is currently defined for Iterable<T>
were an operator that for
loops used, when available, in place of Iterable<T>.iterator()
, a List<T>.forEach
extension of such operator could use indexes.
This is how it’d look like:
inline operator fun <T> List<T>.forEach(action: (T) -> Unit) {
for (i in indices) action(get(i))
}
fun foo() {
val myList = listOf("a", "b", "c")
// The expression that we're iterating is `myList`, whose type is
// `List<*>`, so the compiler would choose the `List<T>.forEach`
// operator defined above instead of creating an iterator.
for (item in myList)
println(item)
}
The same would apply to Array
s as well. We all know how much overhead creating objects does cause (more pressure to the GC, more memory being used, CPU time wasted creating and initializing the object, and so forth) — such design would allow to improve the performance of the for
loop and the forEach
extension for types that support random access. The jitter might optimize things while running even if using iterators, but this proposal would be trivial to implement and is an opportunity to emit more efficient bytecode in the first place.
Admittedly, there are some aspects that need to be defined, such as the forEach
behavior when the list/array is modified while being iterated. However, I think this solution is worth being considered and worked on to define it better.
For backward–compatibility, a forEach
method that is not defined with the operator
keyword is allowed but ignored by for
statements. When there is no forEach
operator available for the type being iterated, the type is required to implement Iterable<T>
and the compiler reverts to using Iterable<T>.iterator()
.
Regarding to targets other than the JVM, I’m not currently aware of how things work in Kotlin/Native, but I can definitely speak for Kotlin/JS — the forEach
operators for List<T>
and Array<T>
would be expect
definitions and each platforms would implement them the best. In the JVM, it would look like I’ve described, while in JS they would be defined as inline intrinsics and the compiler would emit for..of
JS statements directly.