Perusing the Jetpack Compose source code, one stum...
# stdlib
o
Perusing the Jetpack Compose source code, one stumbles upon specialized collection functions such as
List.fastForEach
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/snapshots/ListUtils.kt;l=27
Copy code
/**
 * Iterates through a [List] using the index and calls [action] for each item.
 * This does not allocate an iterator like [Iterable.forEach].
 */
@OptIn(ExperimentalContracts::class)
internal inline fun <T> List<T>.fastForEach(action: (T) -> Unit) {
    contract { callsInPlace(action) }
    for (index in indices) {
        val item = get(index)
        action(item)
    }
}
Is this something that the standard library might be interested in? Yes/no and why?
j
Hard no. Breaks list polymorphism unless you first check for random access (which they don't)
This is a terribly named function in the Compose codebase
It should be called forEachByIndex or indexedForEach
It is absolutely not fast in 100% of cases
e
it also doesn't have the modCount checking that iterator() has
m
And btw. the kotlin compiler AFAIK now does replace the iterator with indices (or if not prbly some other layer like JVM or R8 would) so I don't see a theoretical advantage of such function.
g
We had a difficult time in choosing the name for this and we had all of the arguments that Jake said. When I first wrote
fastForEach
it was named something like
forEachByIndex
, but that quickly gets confusing when you have
forEachIndexedByIndex
.
I wrote it because we had significant performance gains when avoiding iterator allocations. I was doing performance optimizations and didn't want to spend the performance on checking for
RandomAccess
which can be quite expensive, and I didn't want my
inline
function to be bloated with two implementations. The "fast" prefix was the best we could come up with at the time. Naming is hard. We don't expect application developers to need the performance gains from iterating over lists with indices. I had a similar experience with
ArrayList
and found that it is slower than having my own implementation, so we also have
MutableVector
, which is essentially similar, but with no interfaces. I believe the main performance gain from that is the lack of "invoke virtual" because everything is final.
For the standard library, I could imagine performance testing and seeing if a check for
RandomAccess
and then choosing to implement with an index or iterator would be worth while in a general case. I don't know enough about differences between ART and JavaVM and native implementations to know if there is a common implementation that is best for all of them or if it would have to be different to get the best performance. This takes work and maintenance.
e
it would be a behavior change if stdlib forEach started indexing instead of iterating, even for RandomAccess or ArrayList, due to most iterators checking modCount and indexing not. it should be a safe optimization on ImmutableList but that isn't standard…
I dunno, maybe call it
Copy code
/**
 * Even more so than usual, do not modify the underlying list while it is being accessed.
 */
fun <T> List<T>.unsafeForEach(...)