跪拜 Guibai
← All articles
Kotlin · Android

Kotlin's Collection Slicing Functions Are a Masterclass in Performance Specialization

By RockByte ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Understanding these specialization patterns helps developers write collection-heavy code that avoids accidental O(n²) behavior, especially when chaining operations on large lists or custom iterables. The same runtime type-checking technique is reusable in any performance-sensitive Kotlin library.

Summary

Kotlin's collection extraction functions share a common design pattern: they inspect the receiver's runtime type and branch into specialized implementations. A `subList` returns a lightweight view that holds only a reference and an offset, making it an O(1) operation that never copies elements. When `slice` receives a contiguous range, it delegates to `subList` first, then copies the view into a new list via `toList()`.

Functions like `take`, `drop`, and `takeLast` check whether the receiver is a `Collection`, a `List`, or implements `RandomAccess`, and choose between index-based loops, iterator-based traversal, or early-termination paths accordingly. `dropLast` simply reframes the problem as `take((size - n).coerceAtLeast(0))`, reusing the already-optimized `take` logic. `chunked` is a one-liner that delegates to `windowed` with the step equal to the window size and `partialWindows = true`.

The underlying philosophy is that a general API does not require a single general algorithm. Pre-allocating result capacity, breaking early from loops, and selecting traversal strategies based on whether a list supports fast random access all contribute to performance without complicating the public interface.

Takeaways
`subList` returns a view, not a copy; it stores only a reference to the original list and a fromIndex offset, making creation O(1).
Calling `subList` on an existing `SubList` re-computes the offset and points directly to the root list, avoiding nested view objects.
`slice` with an `IntRange` delegates to `subList` then calls `toList()` to produce an independent copy; with arbitrary indices it falls back to a manual loop.
`take(n)` pre-allocates an `ArrayList` of size `n` and breaks the loop immediately after collecting `n` elements, even on infinite-seeming iterables.
`takeLast(n)` checks for `RandomAccess` at runtime: index-based traversal for array-backed lists, iterator-based traversal for linked structures.
`drop(n)` uses the same `RandomAccess` check and falls back to a counter-based skip loop when the receiver is a plain `Iterable`.
`dropLast(n)` is a one-liner that calls `take((size - n).coerceAtLeast(0))`, reusing `take`'s optimizations and safely handling n > size.
`windowed` pre-estimates result capacity, uses `List(size, init)` to snapshot each window via indexed reads when `RandomAccess` is available, and falls back to a `windowedIterator` with a ring buffer for overlapping windows.
`chunked` is simply `windowed(size, size, partialWindows = true)`, making non-overlapping chunks a special case of sliding windows.
Kotlin's `List` is a read-only view, not a guarantee of immutability; a `subList` on a `MutableList` can still cause `ConcurrentModificationException`.
Conclusions

The standard library's habit of checking `this is RandomAccess` at runtime is a quiet rejection of the idea that one algorithm fits all data structures—and it costs almost nothing to implement.

Reframing `dropLast` as `take` is a small but sharp design move: it avoids duplicating logic and automatically inherits every optimization already built into `take`.

Pre-allocating `ArrayList` capacity with an estimated count is a pattern that appears across nearly every function here, suggesting that reallocation cost is a first-class concern in the stdlib's design.

Reading these implementations makes clear that AI-generated code samples rarely teach the performance branching that separates a correct function from a fast one.

Concepts & terms
SubList view
A `SubList` is a lightweight wrapper that holds a reference to the original list and an integer offset. Index lookups are translated by adding the offset, so no element data is copied during creation.
RandomAccess marker interface
A marker interface in Kotlin/Java indicating that a List supports fast (typically O(1)) indexed access. The stdlib checks for this at runtime to choose between index-based loops and iterator-based traversal.
coerceAtLeast
A Kotlin extension function that returns the larger of the receiver value and a given minimum, used in `dropLast` to clamp `size - n` to zero and prevent negative arguments to `take`.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗