Skip to content

Beginner: Kotlin · Classes, objects and collections · topic 13 of 50 · 30 min

Collection operations

Explanation

Kotlin collections have powerful built-in functions that take lambdas: map transforms, filter keeps matching items, sumOf adds, count counts, sortedBy and sortedByDescending sort, groupBy groups, associateBy builds a map, first and firstOrNull find, and any, all and none check conditions. Chaining them replaces most hand-written loops with short, readable code.

Why operations instead of loops?

A loop says how to do something step by step. A collection operation says what you want: "the names of paid orders over Rs 500, sorted by amount". The code is shorter, reads like the requirement, and leaves fewer places for off-by-one mistakes. Every operation below returns a new collection or value; the original list is not changed.

kotlin
data class Order(val id: Int, val customer: String, val amount: Int, val paid: Boolean, val city: String)

val orders = listOf(
    Order(1, "Asha", 450, true, "Pune"),
    Order(2, "Ravi", 1200, false, "Mumbai"),
    Order(3, "Meena", 799, true, "Pune"),
    Order(4, "Tom", 2300, true, "Mumbai"),
)
The sample data used in the examples below.

Transform and filter

kotlin
val paid = orders.filter { it.paid }                         // 3 orders
val names = orders.map { it.customer }                        // [Asha, Ravi, Meena, Tom]
val bigPaidNames = orders.filter { it.paid && it.amount > 500 }.map { it.customer }
println(bigPaidNames)                                         // [Meena, Tom]
filter keeps items where the lambda is true; map turns each item into something else.

Totals and counts

kotlin
println(orders.sumOf { it.amount })           // 4749
println(orders.count { !it.paid })            // 1
println(orders.maxOf { it.amount })           // 2300
println(orders.maxByOrNull { it.amount }?.customer)   // Tom
println(orders.map { it.amount }.average())   // 1187.25
sumOf, count, maxOf and maxByOrNull all take a lambda.

Sorting

kotlin
val cheapFirst = orders.sortedBy { it.amount }.map { it.id }            // [1, 3, 2, 4]
val richFirst = orders.sortedByDescending { it.amount }.map { it.id }   // [4, 2, 3, 1]
sortedBy returns a new sorted list. (sort() on a MutableList sorts in place.)

Finding and checking

kotlin
val firstPune = orders.first { it.city == "Pune" }        // Order 1
val inDelhi = orders.firstOrNull { it.city == "Delhi" }    // null
println(orders.any { it.amount > 2000 })    // true
println(orders.all { it.amount > 100 })     // true
println(orders.none { it.city == "Goa" })   // true
first throws NoSuchElementException if nothing matches; firstOrNull returns null.

Grouping and building maps

kotlin
val byCity: Map<String, List<Order>> = orders.groupBy { it.city }
println(byCity.mapValues { (_, list) -> list.sumOf { it.amount } })   // {Pune=1249, Mumbai=3500}

val byId: Map<Int, Order> = orders.associateBy { it.id }
println(byId[3]?.customer)    // Meena
groupBy makes a map from a key to a list; associateBy makes a map from a key to one item.
OperationReturns
map, filter, sortedBy, take(n), distinct()A new List
sumOf, count, maxOf, averageA number
first, firstOrNull, findOne item (or null)
any, all, noneBoolean
groupBy, associateByA Map
Think of it like this: Collection operations are like an assembly line in a factory. Items go in at one end; one station removes faulty pieces (filter), the next paints each piece (map), the last one packs them in order (sortedBy). Each station does one simple job, and you can add or remove stations without rebuilding the whole line.

Read a chain from top to bottom, one step per line, as in the examples above. When the result is wrong, check each step separately: put the first two steps in a val, print it, then add the next step. This makes chains easy to debug.

Sequences for big data

Each step in a chain creates a new list. For a few thousand items that is fine. For very large collections, or when you only need the first few results, call asSequence() first: steps are then applied lazily, item by item, without building intermediate lists. Finish with toList() or another terminal operation.

kotlin
val firstThreeBig = (1..1_000_000).asSequence()
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .take(3)
    .toList()               // [6, 12, 18]: only a few items are processed
Lazy evaluation stops as soon as three results are found.

Common mistakes

  • Using first { } when there might be no match: it throws. Use firstOrNull.
  • Calling sortedBy and expecting the original list to change. It returns a new list.
  • Very long chains that are hard to read. Name intermediate results with val.
  • Calling .size on the result of filter just to count: use count { } directly.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Collection operations · 1/7

What, not how

Collection operations say what you want instead of how to loop. They return new collections and leave the original unchanged.

Prefer a video by a teacher? Find free videos on “kotlin collection functions map filter groupBy sortedBy” on YouTube ↗

In real life: A daily sales report

A clothing store chain gets every sale of the day as a list. The manager's app shows: total sales, sales per store sorted from highest, the best-selling item, and whether any refund is above Rs 5,000. Each figure is one short chain of collection operations instead of nested loops.

kotlin
data class Sale(val store: String, val item: String, val amount: Int)

fun main() {
    val sales = listOf(Sale("Andheri", "Kurta", 899), Sale("Bandra", "Saree", 2499),
        Sale("Andheri", "Kurta", 899), Sale("Bandra", "Dupatta", 399))
    println("Total: ${sales.sumOf { it.amount }}")
    sales.groupBy { it.store }
        .mapValues { (_, s) -> s.sumOf { it.amount } }
        .entries.sortedByDescending { it.value }
        .forEach { println("${it.key}: ${it.value}") }
    println(sales.groupingBy { it.item }.eachCount().maxByOrNull { it.value }?.key)   // Kurta
}
Total: 4696, then Bandra: 2898, Andheri: 1798, then Kurta.

Practice (0/3 done)

Task 1 · Question

What does this print? val nums = listOf(5, 12, 7, 20, 3) println(nums.filter { it > 6 }.map { it / 2 })

Task 2 · Question

What does listOf("ant", "bee", "cat").firstOrNull { it.startsWith("d") } return?

Task 3 · Practice

Given a list of data class Student(name, city, marks), write one chain for each: (1) names of students with 75+ sorted by marks descending, (2) average marks per city, (3) the topper's name, (4) whether anyone failed (below 33), (5) a Map from name to Student.

Quick quiz

Question 1

1.Does orders.sortedBy { it.amount } change orders?

Question 2

2.Which operation turns List<Order> into Map<String, List<Order>>?

Question 3

3.What does listOf(2, 4, 6).all { it % 2 == 0 } return?

Question 4

4.When is asSequence() helpful?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Does orders.sortedBy { it.amount } change orders?

Answer: No, it returns a new sorted list

Most collection operations return new collections.

Which operation turns List<Order> into Map<String, List<Order>>?

Answer: groupBy

groupBy collects items under each key.

What does listOf(2, 4, 6).all { it % 2 == 0 } return?

Answer: true

Every item is even.

When is asSequence() helpful?

Answer: For very large collections or when only a few results are needed

Sequences avoid building intermediate lists.

What does find return when nothing matches?Related

Answer: undefined

find returns undefined; findIndex returns -1.

From Array methods: map, filter, reduce and friends

Stream.of(1, 2, 3).map(n -> n * 10).filter(n -> n > 10).toList() is…Related

Answer: [20, 30]

Map to 10, 20, 30, then keep those above 10.

From Streams: filter, map and collect

What does find return when nothing matches?Related

Answer: undefined

find gives undefined when no item passes the test (indexOf is the one that gives -1).

From Arrays and array methods

Which LINQ method transforms each item into something else?Related

Answer: Select

Select maps each item to a new value.

From LINQ basics: query data in a few lines

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Classes, objects and collections”. The module exam comes next.
  2. Module 3 exam: Classes, objects and collections · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Coroutines and Flow. 50 topics left in the course before the final exam.

← Previous: Collections: List, Set and Map