Most business logic is data transformation; the collection API turns loops into clear pipelines.
Kotlin's standard library has a rich set of extension functions on collections. Each returns a new collection:
- Transform:
map,mapNotNull,flatMap,mapIndexed. - Filter:
filter,filterNot,filterIsInstance<T>(),take,drop,distinct,distinctBy. - Search:
first { },firstOrNull { },find,any,all,none,count { }. - Aggregate:
sum,sumOf { },maxByOrNull,minOf,average,fold(initial) { acc, x -> },reduce. - Group:
groupBy { key }givesMap<K, List<T>>;associateBy,associateWith,partition { }gives a pair of lists;chunked,windowed,zip. - Sort:
sorted(),sortedBy { },sortedWith(compareBy({ it.city }, { it.name })). - Output:
joinToString,toSet,toMap.
Collection operations are eager: each step builds an intermediate list. For long chains over large data, or infinite data, use a sequence: list.asSequence().filter { }.map { }.take(10).toList(). Sequences process elements one at a time lazily, like Java streams, and stop as soon as possible.
first() throws on an empty match; firstOrNull() returns null. Prefer sumOf { it.price } over map { it.price }.sum(). Readability first: a short for loop is fine when a chain would get clever.
Example
data class Person(val name: String, val city: String, val age: Int)
fun main() {
val people = listOf(
Person("Ada", "London", 36), Person("Alan", "London", 41),
Person("Grace", "NYC", 85), Person("Linus", "Helsinki", 28),
)
println(people.filter { it.age > 30 }.map { it.name }.sorted()) // [Ada, Alan, Grace]
println(people.groupBy { it.city }.mapValues { it.value.size }) // {London=2, NYC=1, Helsinki=1}
println(people.sumOf { it.age }) // 190
println(people.maxByOrNull { it.age }?.name) // Grace
val (young, old) = people.partition { it.age < 40 }
println("${young.size} ${old.size}") // 2 2
println(people.associateBy { it.name }["Linus"]?.city) // Helsinki
println(listOf(1, 2, 3).fold(10) { acc, x -> acc + x }) // 16
println((1..10).chunked(4)) // [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
val firstSquares = generateSequence(1) { it + 1 }
.map { it * it }.filter { it % 2 == 1 }.take(3).toList()
println(firstSquares) // [1, 9, 25]
}Remember
- map/filter/groupBy/fold build new collections
- Prefer firstOrNull, maxByOrNull to avoid exceptions on empty input
- Collection operations are eager; asSequence() makes them lazy
- Use sumOf, associateBy, partition instead of manual loops
Watch out for
- Chaining many operations over huge lists without asSequence(), creating large intermediate lists.