Kotlin: Kotlin集合操作详解

最后更新:2026-08-26

Kotlin 的集合操作让 Charlie 用声明式流水线替代命令式循环—— 一行代码代替 15 行 for 循环,且意图更清晰。

1. 你将学到


2. 一个架构师的真实故事

(1) 痛点:百万级订单处理内存溢出

Charlie 用 处理百万级订单数据,每个中间操作都创建新集合,内存占用 3GB 导致 OOM。

(2) Sequence 惰性求值的解法

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 像流水线:每个元素走完全程才处理下一个,无需中间集合。


3. 核心操作符

(1) map — 转换

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 — 过滤

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 — 展平转换

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 — 分组

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 — 关联映射

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) 操作符速查表

操作符 功能 输入→输出 类比 SQL
转换 SELECT
过滤 WHERE
展平转换 JOIN + SELECT
分组 <T> GROUP BY
关联映射 -
去重 DISTINCT
排序 ORDER BY

4. 集合操作流水线

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 惰性求值

(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) 何时使用 Sequence

场景 用 List 用 Sequence
数据量 < 10,000 > 10,000
操作步数 1-2 步 3+ 步
中间结果大小 与原集合相近 显著缩小
是否需要多次遍历

(3) List vs Sequence 性能对比

维度 List(Eager) Sequence(Lazy)
中间集合 每步创建 不创建
内存 O(n × 步数) O(1)
短路操作 不优化 优化(如 只处理 2 个)
首次结果 等全部处理完 立即可用

6. 聚合操作

(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) 便捷聚合

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 对比

维度
初始值 必须提供 隐式用第一个元素
空集合 安全(返回初始值) 抛异常
返回类型 可与元素类型不同 与元素类型相同
推荐度 ⭐⭐⭐ ⭐⭐

7. 不可变 vs 可变集合

(1) 只读与可变接口

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) 防御性拷贝

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) 集合类型对比

类型 只读接口 可变接口 工厂函数
List /
Set /
Map /

8. 完整示例:百万订单处理流水线

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" }}")
}

输出:

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

❓ 常见问题

Q Kotlin 集合操作 vs Java Stream 有什么区别?
A Kotlin 集合操作更简洁( 关键字、尾 Lambda),不需要 和 转换,且 Sequence 类似 Stream 但更轻量。
Q 什么时候用 Sequence?
A 数据量大(>10,000)、操作步骤多(3+步)、或需要短路操作(//)时用 Sequence。小数据用 List 更简单。
Q 会复制数据吗?
A 会。 创建新列表,是防御性拷贝。如果只需要只读视图且不需要副本,直接用 接口引用即可。
Q 和 选哪个?
A 优先 fold,因为它可以指定初始值且对空集合安全。reduce 在空集合时会抛异常。
Q Kotlin 的 List 是真正不可变的吗?
A 不是。Kotlin 的 是只读接口(不可修改),但底层实现可能是可变的。真正不可变需要用 创建副本。
Q 和 + 有区别吗?
A 功能等价, 更高效(一步完成)。 是 + 的组合简写。

📖 小节


📝 作业

  1. 基础题(难度⭐):用 和 从订单列表中提取所有金额 > 1000 USD 的订单 ID。提示:
  2. 进阶题(难度⭐⭐):用 + 计算每个客户的总消费金额,并按金额降序排列。提示:
  3. 挑战题(难度⭐⭐⭐):用 实现百万级订单的惰性处理流水线:filter → map → take(100),打印实际处理的元素数量。提示:用 计数

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

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

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