Kotlin's Collection Operators Are Wrappers Around a Single Delegate Pattern
Understanding the delegate pattern means you can use filterTo, mapTo, and friends directly to reuse destination collections and avoid unnecessary intermediate allocations — a common need in performance-sensitive Android and server-side Kotlin code. The inline mechanism also clarifies why these operators carry zero abstraction penalty and work seamlessly inside coroutines and Compose.
Every major Kotlin collection operator — map, flatMap, groupBy, zip, and filter — shares a consistent two-layer design. The public function is a thin wrapper that creates an optimized destination collection (an ArrayList sized correctly when the source is a Collection, defaulting to 10 otherwise) and immediately hands off to a *To function. The *To variant runs the actual loop, applying the lambda and populating the mutable destination.
This delegation pattern means the standard library avoids duplicating iteration logic. mapTo, flatMapTo, groupByTo, filterTo, and zip's transform overload each contain the sole implementation; the convenience functions just supply the right starting container. For zip, the simple infix version is itself a delegation to the transform overload with a default Pair-construction lambda.
Performance hinges on two details: the inline modifier eliminates lambda object allocations at every call site, and the collectionSizeOrDefault check lets map and zip pre-allocate ArrayLists to their exact final size when the source is a Collection. The inline keyword also explains why suspend and @Composable calls work inside these lambdas — the lambda body is copied into the caller's context, inheriting its capabilities.
Kotlin's collection operators are not magic — they are thin, predictable wrappers around mutable-collection loops, which makes their performance characteristics easy to reason about.
The consistent *To delegation pattern is a design choice that rewards developers who learn it: you can call mapTo or filterTo directly with a reused ArrayList to cut allocations in hot paths.
Pre-allocation via collectionSizeOrDefault is a small but effective optimization that only works when the source is a Collection; Sequences and other arbitrary Iterables fall back to a default capacity of 10.
The LinkedHashMap choice in groupBy is a deliberate semantic guarantee, not an implementation detail — it ensures deterministic iteration order across runs and platforms.
Marking these operators as inline solves two problems at once: zero-cost lambda abstraction and context inheritance for coroutines and Compose, which would be impossible with regular higher-order functions.