404 Not Found

404 Not Found


nginx

Kotlin Collection Operations Explained

Kotlin's collection operations let Charlie replace imperative loops with declarative pipelines — one line replaces 15 lines of for-loop, with clearer intent.

1. What You'll Learn


2. A Real Architect's Story

(1) Pain Point: OOM When Processing Millions of Orders

Charlie used List to process millions of order records. Every intermediate operation created a new collection, consuming 3GB of memory and causing OOM.

(2) The Sequence Lazy Evaluation Solution

KOTLIN
// Eager: creates intermediate collections at each step
orders.map { enrich(it) }       // Collection 1: 1M elements
      .filter { it.total > 100 } // Collection 2: ~500K elements
      .toList()                   // Collection 3

// Lazy: processes one element through entire pipeline
orders.asSequence()
      .map { enrich(it) }
      .filter { it.total > 100 }
      .toList()  // Only 1 final collection

Sequence is like an assembly line: each element goes through the entire pipeline before the next one starts — no intermediate collections needed.


3. Core Operators

(1) map — Transform

KOTLIN
val orders = listOf(
    Order("ORD-001", 299.99, "Alice"),
    Order("ORD-002", 1_500.00, "Bob")
)

// Transform each element
val ids = orders.map { it.id }                // [ORD-001, ORD-002]
val summaries = orders.map { "${it.id}: \$${it.total} USD" }

// mapIndexed: with index
orders.mapIndexed { i, order -> "[${i + 1}] ${order.id}" }

// mapNotNull: transform + filter nulls
val emails = orders.mapNotNull { it.customerEmail }

(2) filter — Filter

KOTLIN
// Filter by predicate
val highValue = orders.filter { it.total > 1_000 }
val pending = orders.filter { it.status == "PENDING" }

// filterNot: inverse filter
val active = orders.filterNot { it.status == "CANCELLED" }

// filterIndexed: with index
orders.filterIndexed { i, _ -> i % 2 == 0 }  // Even-indexed orders

(3) flatMap — Flatten Transform

KOTLIN
data class Customer(val name: String, val orders: List<Order>)

val customers = listOf(
    Customer("Alice", listOf(Order("ORD-001", 299.99), Order("ORD-002", 150.0))),
    Customer("Bob", listOf(Order("ORD-003", 1_500.00)))
)

// Map + Flatten in one step
val allOrders = customers.flatMap { it.orders }
// [Order(ORD-001, 299.99), Order(ORD-002, 150.0), Order(ORD-003, 1500.0)]

(4) groupBy — Group

KOTLIN
// Group by key
val byCustomer: Map<String, List<Order>> = orders.groupBy { it.customer }

// Group by with value transform
val totalsByCustomer = orders.groupBy(
    keySelector = { it.customer },
    valueTransform = { it.total }
)
// {Alice=[299.99], Bob=[1500.0]}

(5) associate — Associate to Map

KOTLIN
// Create map from list
val orderMap = orders.associate { it.id to it }
// {ORD-001=Order(...), ORD-002=Order(...)}

// associateBy: key selector
val byId = orders.associateBy { it.id }

// associateBy with value transform
val totalsById = orders.associateBy(
    keySelector = { it.id },
    valueTransform = { it.total }
)

(6) Operator Quick Reference

Operator Function Input → Output SQL Analogy
map Transform List<A>List<B> SELECT
filter Filter List<T>List<T> WHERE
flatMap Flatten transform List<A>List<B> JOIN + SELECT
groupBy Group List<T>Map<K, List<T>> GROUP BY
associate Map List<T>Map<K, V> -
distinct Deduplicate List<T>List<T> DISTINCT
sortedBy Sort List<T>List<T> ORDER BY

4. Collection Operation Pipeline

100%
flowchart LR
    A[Orders<br/>1M records] --> B[filter<br/>total > 1000]
    B --> C[map<br/>extract customer]
    C --> D[distinct<br/>unique customers]
    D --> E[groupBy<br/>by region]
    E --> F[Result<br/>Map of customers]

5. Sequence Lazy Evaluation

(1) Eager vs Lazy

KOTLIN
// Eager (List): each step creates new collection
val result = orders
    .map { println("map: ${it.id}"); it.copy(total = it.total * 0.9) }
    .filter { println("filter: ${it.id}"); it.total > 100 }
    .take(2)
    .toList()

// Lazy (Sequence): processes one element at a time
val result2 = orders.asSequence()
    .map { println("seq-map: ${it.id}"); it.copy(total = it.total * 0.9) }
    .filter { println("seq-filter: ${it.id}"); it.total > 100 }
    .take(2)
    .toList()
// Sequence only processes elements until take(2) is satisfied

(2) When to Use Sequence

Scenario Use List Use Sequence
Data size < 10,000 > 10,000
Operation steps 1-2 steps 3+ steps
Intermediate result size Close to source size Significantly reduced
Multiple traversals needed Yes No

(3) List vs Sequence Performance

Dimension List (Eager) Sequence (Lazy)
Intermediate collections Created at every step Not created
Memory O(n × steps) O(1)
Short-circuit operations Not optimized Optimized (e.g., take only processes N)
First result Waits for full pipeline Available immediately

6. Aggregation Operations

(1) fold / reduce

KOTLIN
// fold: with initial value
val totalRevenue = orders.fold(0.0) { acc, order -> acc + order.total }

// reduce: first element as initial value
val maxOrder = orders.reduce { max, order ->
    if (order.total > max.total) order else max
}

// foldRight: from end to start
val reversed = orders.foldRight(emptyList<Order>()) { order, acc -> acc + order }

(2) Convenience Aggregation

KOTLIN
val total = orders.sumOf { it.total }
val avg = orders.map { it.total }.average()
val max = orders.maxByOrNull { it.total }
val min = orders.minByOrNull { it.total }
val count = orders.count { it.total > 1_000 }

// Sorting
val sorted = orders.sortedByDescending { it.total }
val top3 = orders.sortedByDescending { it.total }.take(3)

(3) fold vs reduce

Dimension fold reduce
Initial value Must provide Implicit first element
Empty collection Safe (returns initial value) Throws exception
Return type Can differ from element type Same as element type
Recommendation ⭐⭐⭐ ⭐⭐

7. Immutable vs Mutable Collections

(1) Read-Only and Mutable Interfaces

KOTLIN
// Read-only (immutable interface)
val list: List<Order> = listOf(Order("ORD-001", 299.99, "Alice"))

// Mutable
val mutableList: MutableList<Order> = mutableListOf()
mutableList.add(Order("ORD-002", 1_500.00, "Bob"))

// Read-only view of mutable list
val readOnly: List<Order> = mutableList  // OK: MutableList extends List
// readOnly.add(...)  // ERROR: List has no add method
mutableList.add(Order("ORD-003", 45.50, "Charlie"))  // Changes readOnly view!

(2) Defensive Copy

KOTLIN
class OrderProcessor(private val _orders: MutableList<Order>) {
    // Defensive copy: expose immutable view
    val orders: List<Order> get() = _orders.toList()

    // Or use immutable view (no copy, but can be cast back)
    val ordersView: List<Order> get() = _orders.toList()
}

(3) Collection Type Comparison

Type Read-Only Interface Mutable Interface Factory Functions
List List MutableList listOf / mutableListOf
Set Set MutableSet setOf / mutableSetOf
Map Map MutableMap mapOf / mutableMapOf

8. Complete Example: Million-Order Processing Pipeline

KOTLIN
// ============================================
// OrderProcessor - Collection Pipeline
// Feature: Process orders with functional operators
// ============================================

data class Order(val id: String, val total: Double, val status: String, val customer: String, val region: String)

fun main() {
    // Simulate order data
    val orders = listOf(
        Order("ORD-001", 299.99, "CONFIRMED", "Alice", "US"),
        Order("ORD-002", 15_000.00, "CONFIRMED", "Bob", "EU"),
        Order("ORD-003", 2_500.00, "PENDING", "Charlie", "US"),
        Order("ORD-004", 45.50, "CANCELLED", "Alice", "ASIA"),
        Order("ORD-005", 8_900.00, "CONFIRMED", "Bob", "EU"),
        Order("ORD-006", 1_200.00, "SHIPPED", "Charlie", "US"),
        Order("ORD-007", 350.00, "CONFIRMED", "Alice", "ASIA"),
        Order("ORD-008", 22_000.00, "PENDING", "Bob", "EU"),
        Order("ORD-009", 750.00, "CONFIRMED", "Charlie", "US"),
        Order("ORD-010", 4_500.00, "SHIPPED", "Alice", "US")
    )

    // Pipeline 1: High-value confirmed orders
    println("=== High-Value Confirmed Orders ===")
    orders.filter { it.status == "CONFIRMED" && it.total > 1_000 }
        .sortedByDescending { it.total }
        .forEach { println("  ${it.id}: \$${it.total} USD (${it.customer})") }

    // Pipeline 2: Revenue by region
    println("\n=== Revenue by Region ===")
    orders.filter { it.status != "CANCELLED" }
        .groupBy { it.region }
        .mapValues { (_, list) -> list.sumOf { it.total } }
        .forEach { (region, revenue) -> println("  $region: \$$revenue USD") }

    // Pipeline 3: Top customers by order count and revenue
    println("\n=== Customer Summary ===")
    orders.filter { it.status != "CANCELLED" }
        .groupBy { it.customer }
        .map { (customer, list) ->
            val count = list.size
            val total = list.sumOf { it.total }
            val avg = list.map { it.total }.average()
            "$customer: $count orders, \$${total} USD total, \$${"%.2f".format(avg)} avg"
        }
        .forEach { println("  $it") }

    // Pipeline 4: Using Sequence for efficient processing
    println("\n=== Top 3 Orders (Sequence) ===")
    orders.asSequence()
        .filter { it.status != "CANCELLED" }
        .sortedByDescending { it.total }
        .take(3)
        .forEach { println("  ${it.id}: \$${it.total} USD") }

    // Aggregate: fold to build a summary string
    val summary = orders
        .filter { it.status != "CANCELLED" }
        .fold("Order Summary: ") { acc, order -> "$acc\n  ${order.id} (\$${order.total} USD)" }
    println("\n${summary}")
    println("Total orders: ${orders.size}, Active: ${orders.count { it.status != "CANCELLED" }}")
}

Output:

TEXT
=== High-Value Confirmed Orders ===
  ORD-005: $8900.0 USD (Bob)
  ORD-002: $15000.0 USD (Bob)

=== Revenue by Region ===
  US: $7599.99 USD
  EU: $46400.0 USD
  ASIA: $350.0 USD

=== Customer Summary ===
  Alice: 2 orders, $3649.99 USD total, $1824.995 avg
  Bob: 3 orders, $46400.0 USD total, $15466.666666666666 avg
  Charlie: 3 orders, $4450.0 USD total, $1483.3333333333333 avg

=== Top 3 Orders (Sequence) ===
  ORD-008: $22000.0 USD (Bob)
  ORD-002: $15000.0 USD (Bob)
  ORD-005: $8900.0 USD (Bob)

Order Summary: 
  ORD-001 ($299.99 USD)
  ORD-002 ($15000.0 USD)
  ORD-003 ($2500.0 USD)
  ORD-005 ($8900.0 USD)
  ORD-006 ($1200.0 USD)
  ORD-007 ($350.0 USD)
  ORD-008 ($22000.0 USD)
  ORD-009 ($750.0 USD)
  ORD-010 ($4500.0 USD)
Total orders: 10, Active: 9

❓ FAQ

Q What's the difference between Kotlin collection operations and Java Stream?
A Kotlin collection operations are more concise (it keyword, trailing lambdas), no need for stream()/collect() conversions, and Sequence is similar to Stream but lighter.
Q When should I use Sequence?
A Use Sequence when data is large (>10,000), there are many operation steps (3+), or you need short-circuiting (take/first). For small data, List is simpler.
Q Does toList() copy data?
A Yes. toList() creates a new list — it's a defensive copy. If you only need a read-only view without a copy, just use a List interface reference.
Q fold or reduce — which should I choose?
A Prefer fold because it allows specifying an initial value and is safe for empty collections. reduce throws an exception on empty collections.
Q Is Kotlin's List truly immutable?
A No. Kotlin's List is a read-only interface (cannot be modified through it), but the underlying implementation may be mutable. True immutability requires creating a copy with toList().
Q Is there a difference between flatMap and map + flatten?
A Functionally equivalent; flatMap is more efficient (done in one step). It's the combined shorthand for map + flatten.

📖 Summary


📝 Exercises

  1. Beginner (⭐): Use filter and map to extract IDs of orders with amounts > 1000 USD from an order list. Hint: orders.filter { }.map { }
  2. Intermediate (⭐⭐): Use groupBy + mapValues to calculate each customer's total spending, sorted by amount in descending order. Hint: groupBy { it.customer }.mapValues { }
  3. Advanced (⭐⭐⭐): Use Sequence to implement a lazy processing pipeline for millions of orders: filter → map → take(100), and print the number of elements actually processed. Hint: use a counter in onEach

← Previous | Next →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏