【Kotlin系列05】集合框架:从Java的冗长到函数式编程的优雅

引言:那个让我重构的千行数据处理代码

还记得刚开始用Kotlin时,我接手了一个数据处理模块。代码是典型的Java风格,充满了for循环和临时变量:

java 复制代码
// Java风格 - 冗长的数据处理
List<User> activeAdultUsers = new ArrayList<>();
for (User user : users) {
    if (user.isActive() && user.getAge() >= 18) {
        activeAdultUsers.add(user);
    }
}

List<String> userNames = new ArrayList<>();
for (User user : activeAdultUsers) {
    userNames.add(user.getName());
}

Collections.sort(userNames);

// 取前10个
List<String> top10 = new ArrayList<>();
for (int i = 0; i < Math.min(10, userNames.size()); i++) {
    top10.add(userNames.get(i));
}

30多行代码,创建了4个临时变量,逻辑分散在多个循环中,维护起来令人头疼。

同事用Kotlin重写后,我惊呆了:

kotlin 复制代码
// Kotlin风格 - 函数式链式调用
val top10 = users
    .filter { it.isActive && it.age >= 18 }
    .map { it.name }
    .sorted()
    .take(10)

仅仅5行代码,没有临时变量,逻辑清晰流畅,可读性极强!这就是Kotlin集合框架的魔法。

今天,我们就来深入探索这个强大而优雅的集合系统。

Kotlin集合体系概览

Kotlin的集合分为两大类:不可变集合 (只读)和可变集合

不可变集合 vs 可变集合

kotlin 复制代码
// 不可变集合(只读)
val readOnlyList = listOf(1, 2, 3)
val readOnlySet = setOf("a", "b", "c")
val readOnlyMap = mapOf("key1" to "value1", "key2" to "value2")

// readOnlyList.add(4)  // ❌ 编译错误:没有add方法

// 可变集合
val mutableList = mutableListOf(1, 2, 3)
val mutableSet = mutableSetOf("a", "b", "c")
val mutableMap = mutableMapOf("key1" to "value1")

mutableList.add(4)  // ✅ OK
mutableSet.remove("a")  // ✅ OK
mutableMap["key3"] = "value3"  // ✅ OK

**最佳实践**:默认使用不可变集合(`listOf`、`setOf`、`mapOf`),只有在需要修改时才使用可变集合。这能避免意外修改,提高代码安全性。

集合的类型层级

接口 不可变实现 可变实现 特点
List listOf() mutableListOf() 有序、可重复
Set setOf() mutableSetOf() 无序、不重复
Map mapOf() mutableMapOf() 键值对、键唯一

List:有序集合

创建List

kotlin 复制代码
// 不可变List
val numbers = listOf(1, 2, 3, 4, 5)
val names = listOf("Alice", "Bob", "Charlie")

// 可变List
val mutableNumbers = mutableListOf(1, 2, 3)
val arrayList = ArrayList<Int>()  // Java的ArrayList

// 空List
val emptyList = emptyList<Int>()
val emptyList2 = listOf<Int>()

// 指定大小的List
val zeros = List(5) { 0 }  // [0, 0, 0, 0, 0]
val squares = List(5) { it * it }  // [0, 1, 4, 9, 16]

List常用操作

kotlin 复制代码
val list = listOf(1, 2, 3, 4, 5)

// 访问元素
println(list[0])           // 1(使用下标)
println(list.first())      // 1
println(list.last())       // 5
println(list.get(2))       // 3

// 安全访问
println(list.getOrNull(10))     // null(越界返回null)
println(list.getOrElse(10) { 0 })  // 0(越界返回默认值)

// 查询
println(list.contains(3))   // true
println(3 in list)          // true(更简洁)
println(list.indexOf(3))    // 2
println(list.isEmpty())     // false
println(list.size)          // 5

// 子列表
println(list.subList(1, 4))  // [2, 3, 4](包含start,不包含end)
println(list.slice(1..3))    // [2, 3, 4]
println(list.take(3))        // [1, 2, 3](前3个)
println(list.takeLast(2))    // [4, 5](后2个)
println(list.drop(2))        // [3, 4, 5](跳过前2个)

可变List操作

kotlin 复制代码
val list = mutableListOf(1, 2, 3)

// 添加
list.add(4)              // [1, 2, 3, 4]
list.add(0, 0)           // [0, 1, 2, 3, 4](在索引0处插入)
list.addAll(listOf(5, 6))  // [0, 1, 2, 3, 4, 5, 6]

// 删除
list.remove(0)           // [1, 2, 3, 4, 5, 6](删除值为0的元素)
list.removeAt(0)         // [2, 3, 4, 5, 6](删除索引0的元素)
list.removeAll { it % 2 == 0 }  // [3, 5](删除偶数)

// 修改
list[0] = 10             // [10, 5]
list.set(1, 20)          // [10, 20]

// 排序
list.sort()              // 原地排序
list.reverse()           // 原地翻转

Set:无重复集合

kotlin 复制代码
// 创建Set
val set = setOf(1, 2, 3, 2, 1)  // 自动去重:{1, 2, 3}
val mutableSet = mutableSetOf("a", "b", "c")

// Set操作
println(set.contains(2))  // true
println(2 in set)         // true

// 可变Set操作
mutableSet.add("d")       // {a, b, c, d}
mutableSet.remove("a")    // {b, c, d}
mutableSet.addAll(listOf("e", "f"))  // {b, c, d, e, f}

// 集合运算
val set1 = setOf(1, 2, 3)
val set2 = setOf(3, 4, 5)

println(set1 union set2)       // {1, 2, 3, 4, 5}(并集)
println(set1 intersect set2)   // {3}(交集)
println(set1 subtract set2)    // {1, 2}(差集)

**使用场景**:当你需要确保元素唯一性时,使用Set。例如:去重、成员检查、集合运算。

Map:键值对集合

创建Map

kotlin 复制代码
// 不可变Map
val map = mapOf(
    "name" to "Alice",
    "age" to 25,
    "city" to "Beijing"
)

// 可变Map
val mutableMap = mutableMapOf(
    1 to "one",
    2 to "two"
)

// 空Map
val emptyMap = emptyMap<String, Int>()

**to的秘密**:`to`是Kotlin的中缀函数,`"key" to "value"`实际上创建了一个`Pair("key", "value")`对象。

Map常用操作

kotlin 复制代码
val map = mapOf(
    "name" to "Alice",
    "age" to 25,
    "city" to "Beijing"
)

// 访问元素
println(map["name"])         // "Alice"
println(map.get("name"))     // "Alice"
println(map["country"])      // null(键不存在)
println(map.getOrDefault("country", "Unknown"))  // "Unknown"
println(map.getValue("name"))  // "Alice"(键不存在会抛异常)

// 查询
println(map.containsKey("name"))    // true
println("name" in map)              // true
println(map.containsValue("Alice")) // true
println(map.isEmpty())              // false
println(map.size)                   // 3

// 遍历
for ((key, value) in map) {
    println("$key: $value")
}

map.forEach { (key, value) ->
    println("$key: $value")
}

// 获取键和值
println(map.keys)    // [name, age, city]
println(map.values)  // [Alice, 25, Beijing]
println(map.entries) // [name=Alice, age=25, city=Beijing]

可变Map操作

kotlin 复制代码
val map = mutableMapOf(
    "name" to "Alice",
    "age" to 25
)

// 添加/修改
map["city"] = "Beijing"       // 添加新键值对
map["age"] = 26               // 修改已有键的值
map.put("email", "alice@example.com")
map.putAll(mapOf("country" to "China", "job" to "Engineer"))

// 删除
map.remove("email")           // 删除键为email的条目
map.remove("name", "Bob")     // 仅当键为name且值为Bob时删除(返回false)

// 条件操作
map.putIfAbsent("name", "Bob")  // name已存在,不会修改

集合操作符:函数式编程的精华

这是Kotlin集合最强大的部分!通过链式调用操作符,可以用极其简洁的代码完成复杂的数据处理。

转换操作符

map - 映射转换

将集合中的每个元素转换为另一种形式。

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// 转换为平方
val squares = numbers.map { it * it }
println(squares)  // [1, 4, 9, 16, 25]

// 转换为字符串
val strings = numbers.map { "Number $it" }
println(strings)  // [Number 1, Number 2, Number 3, Number 4, Number 5]

// 实际应用:提取对象属性
data class User(val name: String, val age: Int)
val users = listOf(
    User("Alice", 25),
    User("Bob", 30),
    User("Charlie", 35)
)

val names = users.map { it.name }
println(names)  // [Alice, Bob, Charlie]

mapNotNull - 映射并过滤null

kotlin 复制代码
val list = listOf("1", "2", "abc", "3")

val numbers = list.mapNotNull { it.toIntOrNull() }
println(numbers)  // [1, 2, 3]("abc"被过滤掉)

flatMap - 扁平化映射

将每个元素映射为一个集合,然后合并所有集合。

kotlin 复制代码
val list = listOf(
    listOf(1, 2, 3),
    listOf(4, 5),
    listOf(6, 7, 8)
)

// map vs flatMap
val mapped = list.map { it.map { num -> num * 2 } }
println(mapped)  // [[2, 4, 6], [8, 10], [12, 14, 16]](嵌套列表)

val flatMapped = list.flatMap { it.map { num -> num * 2 } }
println(flatMapped)  // [2, 4, 6, 8, 10, 12, 14, 16](扁平列表)

// 实际应用:一对多关系
data class Department(val name: String, val employees: List<String>)
val departments = listOf(
    Department("IT", listOf("Alice", "Bob")),
    Department("HR", listOf("Charlie", "David"))
)

val allEmployees = departments.flatMap { it.employees }
println(allEmployees)  // [Alice, Bob, Charlie, David]

过滤操作符

filter - 过滤

保留符合条件的元素。

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 过滤偶数
val evens = numbers.filter { it % 2 == 0 }
println(evens)  // [2, 4, 6, 8, 10]

// 过滤奇数
val odds = numbers.filter { it % 2 != 0 }
println(odds)  // [1, 3, 5, 7, 9]

// 实际应用:过滤用户
val users = listOf(
    User("Alice", 25),
    User("Bob", 17),
    User("Charlie", 30)
)

val adults = users.filter { it.age >= 18 }
println(adults)  // [User(Alice, 25), User(Charlie, 30)]

filterNot - 反向过滤

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)
val notEvens = numbers.filterNot { it % 2 == 0 }
println(notEvens)  // [1, 3, 5]

filterNotNull - 过滤null

kotlin 复制代码
val list = listOf(1, null, 2, null, 3)
val nonNulls = list.filterNotNull()
println(nonNulls)  // [1, 2, 3]

take / drop - 取/跳过元素

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

println(numbers.take(3))       // [1, 2, 3](前3个)
println(numbers.takeLast(3))   // [8, 9, 10](后3个)
println(numbers.drop(3))       // [4, 5, 6, 7, 8, 9, 10](跳过前3个)
println(numbers.dropLast(3))   // [1, 2, 3, 4, 5, 6, 7](跳过后3个)

// 条件take/drop
println(numbers.takeWhile { it < 5 })  // [1, 2, 3, 4]
println(numbers.dropWhile { it < 5 })  // [5, 6, 7, 8, 9, 10]

聚合操作符

sum / average / max / min

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

println(numbers.sum())      // 15
println(numbers.average())  // 3.0
println(numbers.max())      // 5(Kotlin 1.4+已弃用,使用maxOrNull())
println(numbers.maxOrNull())  // 5
println(numbers.minOrNull())  // 1

count - 计数

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

println(numbers.count())  // 10(总数)
println(numbers.count { it % 2 == 0 })  // 5(偶数个数)

reduce / fold - 累积计算

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// reduce: 从第一个元素开始累积
val sum = numbers.reduce { acc, num -> acc + num }
println(sum)  // 15

// fold: 指定初始值
val sumWithInitial = numbers.fold(100) { acc, num -> acc + num }
println(sumWithInitial)  // 115(100 + 15)

// 实际应用:计算总价
data class Product(val name: String, val price: Double)
val cart = listOf(
    Product("Book", 29.9),
    Product("Pen", 5.5),
    Product("Notebook", 15.0)
)

val totalPrice = cart.fold(0.0) { total, product -> total + product.price }
println("Total: $$totalPrice")  // Total: $50.4

分组操作符

groupBy - 分组

按条件将集合分组。

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// 按奇偶分组
val grouped = numbers.groupBy { it % 2 == 0 }
println(grouped)  // {false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}

// 实际应用:按年龄段分组
data class Person(val name: String, val age: Int)
val people = listOf(
    Person("Alice", 25),
    Person("Bob", 17),
    Person("Charlie", 30),
    Person("David", 16),
    Person("Eve", 28)
)

val ageGroups = people.groupBy {
    when {
        it.age < 18 -> "未成年"
        it.age < 30 -> "青年"
        else -> "成年"
    }
}
println(ageGroups)
// {未成年=[Person(Bob, 17), Person(David, 16)],
//  青年=[Person(Alice, 25), Person(Eve, 28)],
//  成年=[Person(Charlie, 30)]}

partition - 二分

将集合分为符合和不符合条件的两部分。

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5, 6)

val (evens, odds) = numbers.partition { it % 2 == 0 }
println("偶数: $evens")  // 偶数: [2, 4, 6]
println("奇数: $odds")   // 奇数: [1, 3, 5]

排序操作符

kotlin 复制代码
val numbers = listOf(3, 1, 4, 1, 5, 9, 2, 6)

// 升序排序
println(numbers.sorted())  // [1, 1, 2, 3, 4, 5, 6, 9]

// 降序排序
println(numbers.sortedDescending())  // [9, 6, 5, 4, 3, 2, 1, 1]

// 自定义排序
data class User(val name: String, val age: Int)
val users = listOf(
    User("Alice", 25),
    User("Bob", 30),
    User("Charlie", 20)
)

val sortedByAge = users.sortedBy { it.age }
println(sortedByAge)  // [Charlie(20), Alice(25), Bob(30)]

val sortedByName = users.sortedByDescending { it.name }
println(sortedByName)  // [Charlie, Bob, Alice]

查找操作符

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5)

// 查找第一个符合条件的
println(numbers.find { it > 3 })      // 4
println(numbers.firstOrNull { it > 3 })  // 4(同find)

// 查找最后一个符合条件的
println(numbers.lastOrNull { it < 3 })  // 2

// 检查是否存在
println(numbers.any { it > 10 })      // false(是否有任意元素满足)
println(numbers.all { it > 0 })       // true(是否所有元素都满足)
println(numbers.none { it < 0 })      // true(是否没有元素满足)

去重操作符

kotlin 复制代码
val numbers = listOf(1, 2, 2, 3, 3, 3, 4, 4, 4, 4)

// 去重
println(numbers.distinct())  // [1, 2, 3, 4]

// 按条件去重
data class User(val id: Int, val name: String)
val users = listOf(
    User(1, "Alice"),
    User(2, "Bob"),
    User(1, "Alice2")  // id重复
)

val distinctUsers = users.distinctBy { it.id }
println(distinctUsers)  // [User(1, Alice), User(2, Bob)]

链式操作:组合的威力

Kotlin集合操作符的真正威力在于链式组合

kotlin 复制代码
data class Student(val name: String, val age: Int, val scores: List<Int>)

val students = listOf(
    Student("Alice", 20, listOf(85, 90, 92)),
    Student("Bob", 19, listOf(78, 82, 80)),
    Student("Charlie", 21, listOf(92, 95, 98)),
    Student("David", 20, listOf(88, 85, 90))
)

// 需求:找出20岁以上、平均分大于90的学生名字,按平均分降序排列
val result = students
    .filter { it.age >= 20 }                    // 过滤年龄
    .map { it to it.scores.average() }          // 计算平均分
    .filter { (_, avg) -> avg > 90 }            // 过滤平均分
    .sortedByDescending { (_, avg) -> avg }     // 降序排序
    .map { (student, avg) -> "${student.name}: $avg" }  // 格式化输出

println(result)  // [Charlie: 95.0, Alice: 89.0]

**可读性提示**:链式调用很强大,但也要注意可读性。如果链条过长(超过5-6个操作),考虑: 1. 分解为多个步骤,使用中间变量 2. 添加注释说明每个步骤的目的 3. 提取复杂的Lambda为命名函数

Sequence:惰性求值的秘密武器

当处理大量数据时,普通集合操作会创建多个中间集合,影响性能。Sequence提供惰性求值,只在需要时才计算。

Collection vs Sequence

kotlin 复制代码
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

// Collection:立即执行,创建中间集合
val result1 = numbers
    .map {
        println("map: $it")
        it * 2
    }
    .filter {
        println("filter: $it")
        it > 10
    }
    .take(2)
// 输出:map 1, map 2, ... map 10, filter 2, filter 4, ... filter 20

// Sequence:惰性执行,不创建中间集合
val result2 = numbers.asSequence()
    .map {
        println("map: $it")
        it * 2
    }
    .filter {
        println("filter: $it")
        it > 10
    }
    .take(2)
    .toList()  // 终端操作,触发计算
// 输出:map 1, filter 2, map 2, filter 4, ... map 6, filter 12
// 找到2个后就停止

何时使用Sequence

kotlin 复制代码
// ❌ 小集合不需要Sequence
val small = listOf(1, 2, 3).asSequence()  // 过度优化

// ✅ 大集合使用Sequence
val large = (1..1_000_000).asSequence()
    .map { it * 2 }
    .filter { it % 3 == 0 }
    .take(100)
    .toList()

// ✅ 多步操作使用Sequence
val result = someList.asSequence()
    .map { /* 步骤1 */ }
    .filter { /* 步骤2 */ }
    .flatMap { /* 步骤3 */ }
    .distinct()
    .sorted()
    .toList()

// ✅ 可能提前终止的操作
val firstMatch = largeList.asSequence()
    .map { expensiveOperation(it) }
    .find { it > 100 }  // 找到第一个就停止

**注意**:Sequence的惰性求值意味着: 1. 必须有终端操作(`toList()`、`toSet()`、`find()`等)才会执行 2. 不要在Sequence上执行有副作用的操作(如打印日志),因为执行顺序可能不符合预期 3. Sequence不是线程安全的

创建Sequence

kotlin 复制代码
// 从集合转换
val seq1 = listOf(1, 2, 3).asSequence()

// 从生成器
val seq2 = generateSequence(1) { it + 1 }  // 无限序列:1, 2, 3, ...
val first10 = seq2.take(10).toList()

// 斐波那契数列
val fibonacci = generateSequence(Pair(0, 1)) { (a, b) -> Pair(b, a + b) }
    .map { it.first }
val first10Fib = fibonacci.take(10).toList()
println(first10Fib)  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

// 从文件读取行(自动惰性处理)
fun readLargeFile(path: String) {
    File(path).useLines { lines ->
        lines
            .filter { it.isNotEmpty() }
            .map { it.trim() }
            .take(100)
            .forEach { println(it) }
    }
}

实战:电商订单数据分析

让我们用一个完整的实战案例,综合运用所学的集合操作:

kotlin 复制代码
// 数据模型
data class Product(val id: Int, val name: String, val category: String, val price: Double)
data class Order(val orderId: String, val userId: Int, val products: List<Product>, val date: String)
data class User(val userId: Int, val name: String, val city: String)

// 订单数据
val orders = listOf(
    Order("O001", 1, listOf(
        Product(101, "Laptop", "Electronics", 5999.0),
        Product(102, "Mouse", "Electronics", 99.0)
    ), "2024-01-15"),
    Order("O002", 2, listOf(
        Product(201, "Book", "Books", 49.9),
        Product(202, "Pen", "Stationery", 5.0)
    ), "2024-01-16"),
    Order("O003", 1, listOf(
        Product(103, "Keyboard", "Electronics", 299.0)
    ), "2024-01-17"),
    Order("O004", 3, listOf(
        Product(301, "Shirt", "Clothing", 199.0),
        Product(302, "Pants", "Clothing", 299.0)
    ), "2024-01-18")
)

val users = listOf(
    User(1, "Alice", "Beijing"),
    User(2, "Bob", "Shanghai"),
    User(3, "Charlie", "Beijing")
)

// 分析1:计算每个用户的总消费
fun analyzeUserSpending(): Map<String, Double> {
    return orders
        .groupBy { it.userId }
        .mapValues { (_, userOrders) ->
            userOrders.sumOf { order ->
                order.products.sumOf { it.price }
            }
        }
        .mapKeys { (userId, _) ->
            users.find { it.userId == userId }?.name ?: "Unknown"
        }
}

println("用户总消费:")
analyzeUserSpending().forEach { (name, total) ->
    println("  $name: ¥$total")
}
// 输出:
// Alice: ¥6397.0
// Bob: ¥54.9
// Charlie: ¥498.0

// 分析2:找出最受欢迎的商品类别
fun findPopularCategories(): List<Pair<String, Int>> {
    return orders
        .flatMap { it.products }
        .groupBy { it.category }
        .mapValues { (_, products) -> products.size }
        .toList()
        .sortedByDescending { (_, count) -> count }
}

println("\n商品类别销量排行:")
findPopularCategories().forEach { (category, count) ->
    println("  $category: $count 件")
}
// 输出:
// Electronics: 3 件
// Clothing: 2 件
// Books: 1 件
// Stationery: 1 件

// 分析3:计算每个城市的订单总额
fun analyzeCityRevenue(): Map<String, Double> {
    return orders
        .map { order ->
            val user = users.find { it.userId == order.userId }
            val total = order.products.sumOf { it.price }
            user?.city to total
        }
        .filterNotNull()
        .groupBy({ it.first!! }, { it.second })
        .mapValues { (_, totals) -> totals.sum() }
}

println("\n城市订单总额:")
analyzeCityRevenue().forEach { (city, total) ->
    println("  $city: ¥$total")
}
// 输出:
// Beijing: ¥6895.0
// Shanghai: ¥54.9

// 分析4:找出高价值订单(总额>500)的用户
fun findHighValueCustomers(): List<String> {
    return orders
        .filter { order ->
            order.products.sumOf { it.price } > 500
        }
        .map { it.userId }
        .distinct()
        .mapNotNull { userId ->
            users.find { it.userId == userId }?.name
        }
}

println("\n高价值客户:")
findHighValueCustomers().forEach { name ->
    println("  $name")
}
// 输出:
// Alice

常见问题

Q1: 什么时候用List、Set、Map?

选择指南

需求 选择 原因
需要保持元素顺序 List 有序集合
需要通过索引访问 List 支持下标访问
需要去重 Set 自动去重
需要快速检查元素存在性 Set O(1)查找
需要键值对映射 Map 键值对存储
需要按键快速查找值 Map O(1)查找
kotlin 复制代码
// 示例:记录用户访问历史
val visitHistory = mutableListOf<String>()  // ✅ 需要顺序

// 示例:记录访问过的页面(不重复)
val visitedPages = mutableSetOf<String>()  // ✅ 需要去重

// 示例:记录页面访问次数
val pageViews = mutableMapOf<String, Int>()  // ✅ 需要统计

Q2: filter和map的顺序有什么影响?

kotlin 复制代码
val numbers = (1..1000).toList()

// 方案1:先过滤再映射(推荐)
val result1 = numbers
    .filter { it % 2 == 0 }  // 处理1000个元素,保留500个
    .map { it * it }         // 处理500个元素

// 方案2:先映射再过滤
val result2 = numbers
    .map { it * it }         // 处理1000个元素
    .filter { it % 2 == 0 }  // 处理1000个元素

// 方案1更高效:尽早减少数据量

原则先过滤再映射,尽早减少数据量。

Q3: 何时使用Sequence?

kotlin 复制代码
// ❌ 不需要Sequence
listOf(1, 2, 3).asSequence().map { it * 2 }.toList()  // 小集合,overhead更大

// ✅ 需要Sequence
(1..1_000_000)
    .asSequence()          // 大集合
    .map { it * 2 }
    .filter { it > 100 }
    .take(10)              // 提前终止
    .toList()

使用Sequence的场景

  1. 处理大集合(10,000+元素)
  2. 多步操作(3+个操作符)
  3. 可能提前终止(find、take等)
  4. 处理无限序列

Q4: 如何选择可变/不可变集合?

kotlin 复制代码
// ✅ 默认使用不可变
val config = mapOf("host" to "localhost", "port" to 8080)

// ✅ 需要修改时使用可变
val cache = mutableMapOf<String, Any>()
cache["user"] = loadUser()

// ❌ 不要暴露可变集合
class UserManager {
    private val _users = mutableListOf<User>()
    val users: List<User> = _users  // ✅ 只读视图
}

最佳实践

  • 默认使用不可变集合(listOfsetOfmapOf
  • 只在必要时使用可变集合
  • 不要暴露可变集合,返回只读视图

总结

本文深入探索了Kotlin强大的集合框架:

1. 集合体系

  • List(有序可重复)、Set(无序不重复)、Map(键值对)
  • 不可变集合 vs 可变集合
  • 默认使用不可变,提高安全性

2. 集合操作符

  • 转换:map、flatMap、mapNotNull
  • 过滤:filter、filterNot、take、drop
  • 聚合:sum、average、reduce、fold
  • 分组:groupBy、partition
  • 排序:sorted、sortedBy、sortedDescending
  • 查找:find、any、all、none
  • 去重:distinct、distinctBy

3. 函数式编程

  • 链式调用,代码简洁优雅
  • 无副作用,易于理解和测试
  • 声明式而非命令式

4. Sequence惰性求值

  • 不创建中间集合,节省内存
  • 支持提前终止,提高效率
  • 适合大数据和多步操作

5. 最佳实践

  • 优先使用不可变集合
  • 先过滤再映射
  • 大数据使用Sequence
  • 注意链式操作的可读性

下一篇文章我们将学习面向对象进阶:接口、抽象类与多态,深入探索Kotlin的OOP特性。

练习题

练习1:学生成绩管理系统

kotlin 复制代码
data class Student(val id: Int, val name: String, val grades: Map<String, Int>)

val students = listOf(
    Student(1, "Alice", mapOf("Math" to 95, "English" to 88, "Physics" to 92)),
    Student(2, "Bob", mapOf("Math" to 78, "English" to 85, "Physics" to 80)),
    Student(3, "Charlie", mapOf("Math" to 92, "English" to 95, "Physics" to 98))
)

// TODO 1: 找出数学成绩大于90的学生名字

// TODO 2: 计算每个学生的平均分,返回Map<String, Double>

// TODO 3: 找出平均分最高的学生

// TODO 4: 统计每门课的平均分

// TODO 5: 找出所有科目都及格(>=60)的学生

练习2:日志分析系统

kotlin 复制代码
data class LogEntry(val timestamp: String, val level: String, val message: String)

val logs = """
2024-01-15 10:23:45 INFO User login: alice
2024-01-15 10:24:12 ERROR Failed to connect to database
2024-01-15 10:25:30 WARN Memory usage: 85%
2024-01-15 10:26:15 INFO User logout: alice
2024-01-15 10:27:22 ERROR Null pointer exception
""".trimIndent().lines().filter { it.isNotEmpty() }

// TODO 1: 解析日志(提示:使用split)

// TODO 2: 统计各级别日志数量

// TODO 3: 找出所有ERROR级别的消息

// TODO 4: 按小时分组统计日志数量(提示:提取timestamp的小时部分)

练习3:使用Sequence优化性能

kotlin 复制代码
// TODO: 重写以下代码,使用Sequence提高性能
fun findPrimeNumbers(max: Int): List<Int> {
    return (2..max)
        .filter { isPrime(it) }
        .take(100)
}

fun isPrime(n: Int): Boolean {
    if (n < 2) return false
    return (2..Math.sqrt(n.toDouble()).toInt()).none { n % it == 0 }
}

// TODO: 对比性能差异

系列文章导航:


如果这篇文章对你有帮助,欢迎点赞、收藏、分享!有任何问题或建议,欢迎在评论区留言讨论。让我们一起学习,一起成长!

也欢迎访问我的个人主页发现更多宝藏资源

相关推荐
冬奇Lab2 小时前
稳定性性能系列之十四——电量与网络优化:Battery Historian与弱网处理实战
android·性能优化·debug
Coffeeee2 小时前
了解一下Android16更新事项,拿捏下一波适配
android·前端·google
用户41659673693552 小时前
深入解析安卓 ELF 16KB 页对齐:原生编译与脚本修复的权衡
android
恋猫de小郭3 小时前
Compose Multiplatform 1.10 Interop views 新特性:Overlay 和 Autosizing
android·flutter·macos·kotlin·github·objective-c·cocoa
胖虎13 小时前
Android 文件下载实践:基于 OkHttp 的完整实现与思考
android·okhttp·下载文件·安卓下载·安卓中的下载
_李小白3 小时前
【Android 美颜相机】第四天:CameraLoader、Camera1Loader 与 Camera2Loader
android·数码相机
00后程序员张3 小时前
iOS APP 性能测试工具,监控CPU,实时日志输出
android·ios·小程序·https·uni-app·iphone·webview
YIN_尹3 小时前
【MySQL】数据类型(下)
android·mysql·adb
invicinble3 小时前
认识es的多个维度
android·大数据·elasticsearch