第 2 篇|Kotlin 进阶 —— 集合、循环与条件表达式

第 2 篇|Kotlin 进阶 ------ 集合、循环与条件表达式

掌握 Kotlin 集合框架与流程控制,写出简洁高效的 Kotlin 风格代码

哈喽,各位坚持学习的小伙伴们!上一篇我们搞定了 Kotlin 的变量、函数和空安全机制,今天正式进入 Kotlin 进阶篇。这一篇,我们将解锁 Kotlin 中最具生产力的三大核心技能:集合操作、流程控制(条件与循环)、以及让你代码瞬间变优雅的高阶函数。

如果你是从 Java 转过来的,你一定会惊叹:原来操作一个列表可以只用一行链式调用!原来 switch 可以被when 秒成渣!话不多说,我们直接上硬货。


一、Kotlin 集合全景图:可变与不可变,泾渭分明

Kotlin 的集合框架在设计上做了一个非常清晰的切割,这是它区别于 Java 集合的最大亮点之一:

  • 不可变集合(只读集合):List、Set、Map。一旦创建,不能增删改元素,天然线程安全,编译期就能拦截误修改。
  • 可变集合(可读写集合):MutableList、MutableSet、MutableMap。可以自由增删改元素。

这种设计从根源上避免了并发修改异常,也让代码意图一目了然。

1. 集合类型概览

集合类型 不可变版本 可变版本 特点
列表 List MutableList 有序、可重复
集合 Set MutableSet 无序、不可重复
映射 Map MutableMap 键值对存储,键唯一

2. List(列表)------ 有序可重复

kotlin 复制代码
fun main() {
    // 不可变 List:无法修改
    val readOnlyList = listOf("Apple", "Banana", "Orange")
    // readOnlyList.add("Grape") // ❌ 编译报错!Unresolved reference: add
    
    // 可变 MutableList:可以随意增删改
    val mutableList = mutableListOf("Apple", "Banana")
    mutableList.add("Orange")     // ✅ 正确
    mutableList.removeAt(0)       // ✅ 正确
    println(mutableList)          // 输出:[Banana, Orange]
}

3. Set(集合)------ 无序不可重复

kotlin 复制代码
fun main() {
    // 不可变 Set,自动去重
    val readOnlySet = setOf(1, 2, 3, 2, 1)
    println(readOnlySet) // 输出:[1, 2, 3]
    
    // 可变 MutableSet
    val mutableSet = mutableSetOf(1, 2, 3)
    mutableSet.add(4)
    println(mutableSet) // 输出:[1, 2, 3, 4]
}

4. Map(映射/字典)------ 键值对

Kotlin 中创建 Map 的语法非常优雅,使用 to 关键字连接键和值(to 是一个中缀函数,返回 Pair 对象)。

kotlin 复制代码
fun main() {
    // 不可变 Map
    val readOnlyMap = mapOf(
        "name" to "张三",
        "age" to 18,
        "city" to "北京"
    )
    println(readOnlyMap["name"]) // 输出:张三
    
    // 可变 MutableMap
    val mutableMap = mutableMapOf("score" to 90)
    mutableMap["level"] = "优秀"   // 添加新键值对
    mutableMap["score"] = 95       // 修改已有键的值
    println(mutableMap)            // 输出:{score=95, level=优秀}
    
    // 遍历 Map(解构声明)
    for ((key, value) in readOnlyMap) {
        println("$key -> $value")
    }
}

二、为什么选择 Kotlin 集合框架?核心优势深度解析

Kotlin 集合框架并非对 Java 集合的简单封装,而是从设计层面进行了全面优化,解决了 Java 集合的诸多痛点。

1. 明确的可变性控制:从根源避免并发问题

Java 集合的最大痛点之一是可变性模糊:即使你用 Collections.unmodifiableList() 包装,也只是在运行时抛出异常,编译期无法感知。

Java 反面教材

java 复制代码
// Java:编译期无法阻止修改,运行时才报错
List<String> list = new ArrayList<>();
list.add("Apple");
List<String> unmodifiableList = Collections.unmodifiableList(list);
unmodifiableList.add("Banana"); // 编译通过!运行时抛出 UnsupportedOperationException

Kotlin 优雅解决

kotlin 复制代码
// Kotlin:编译期直接报错,从根源避免问题
val list = listOf("Apple", "Banana")
// list.add("Orange") // 编译报错:Unresolved reference 'add'

// 需要修改时,明确使用可变版本
val mutableList = mutableListOf("Apple", "Banana")
mutableList.add("Orange") // 正常执行

2. 内置空安全:彻底告别 NullPointerException

Kotlin 集合框架深度集成了空安全特性,让你在编译期就能发现潜在的空指针问题。

kotlin 复制代码
// 1. 明确区分"可空元素的集合"与"不可空元素的集合"
val listWithNulls: List<String?> = listOf("Apple", null, "Orange") // 允许包含 null
val listWithoutNulls: List<String> = listOf("Apple", "Banana")     // 不允许包含 null

// 2. 安全访问元素
val firstItem = listWithNulls.firstOrNull() // 安全获取,可能为 null

// 3. 便捷的过滤 null 操作
val nonNullList = listWithNulls.filterNotNull() // 自动过滤 null,结果:[Apple, Orange]

3. 丰富的高阶函数:一行代码完成复杂数据处理

这是 Kotlin 集合最强大的优势,没有之一。通过 filter、map、reduce 等高阶函数,能以声明式的方式处理数据,代码可读性提升数倍。

需求:从学生列表中找出及格的男生,按分数降序排列,提取姓名列表。

Java 版本(15+ 行代码):

java 复制代码
// Java:繁琐的循环、临时变量、条件判断
List<Student> students = ...;
List<Student> filtered = new ArrayList<>();
for (Student s : students) {
    if (s.getScore() >= 60 && "男".equals(s.getGender())) {
        filtered.add(s);
    }
}
Collections.sort(filtered, new Comparator<Student>() {
    @Override
    public int compare(Student s1, Student s2) {
        return Integer.compare(s2.getScore(), s1.getScore());
    }
});
List<String> names = new ArrayList<>();
for (Student s : filtered) {
    names.add(s.getName());
}

Kotlin 版本(1 行链式调用):

kotlin 复制代码
// Kotlin:声明式、链式调用,逻辑清晰
val names = students
    .filter { it.score >= 60 && it.gender == "男" }
    .sortedByDescending { it.score }
    .map { it.name }

4. 极简的工厂方法:告别 new 与 add 的冗余

Java 创建集合需要先 new 再 add,代码冗余;Kotlin 提供了极简的工厂方法,一行代码完成创建与初始化。

kotlin 复制代码
// Java 繁琐初始化 vs Kotlin 极简工厂方法
val list = listOf("Apple", "Banana", "Orange")
val map = mapOf("Alice" to 25, "Bob" to 30)

// 甚至支持生成式初始化
val evenNumbers = List(10) { it * 2 } // 生成 [0, 2, 4, ..., 18]

5. 完美的 Java 互操作性:无缝迁移,零成本接入

Kotlin 集合框架基于 Java 集合进行封装,因此与 Java 代码完全互操作。你可以在 Kotlin 中直接使用 Java 集合,也可以在 Java 中直接使用 Kotlin 集合,现有 Java 项目可以逐步迁移到 Kotlin。

kotlin 复制代码
// Kotlin 中调用 Java 方法
val javaList = JavaClass.getJavaList() // 返回 Java 的 List
javaList.forEach { println(it) }       // 可以直接用 Kotlin 高阶函数

// Java 中调用 Kotlin 方法
List<String> kotlinList = KotlinClass.getKotlinList(); // 返回 Kotlin 的 List
kotlinList.add("New Item"); // 可以直接用 Java 方法修改

三、集合创建语法:极简写法告别冗余

Kotlin 提供了极其简洁的集合创建方式,无需像 Java 那样先 new 再 add。

1. List 与 Set 创建

kotlin 复制代码
// 不可变集合
val list = listOf(1, 2, 3)
val set = setOf("Kotlin", "Java", "Python")

// 可变集合
val mutableList = mutableListOf<String>()
val mutableSet = mutableSetOf(10, 20, 30)

// 空集合
val emptyList = emptyList<Int>()
val emptySet = emptySet<String>()

// 过滤 null 的集合
val listNotNull = listOfNotNull(1, null, 3, null) // 结果:[1, 3]

2. Map 创建:键值对的优雅写法

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

// 可变 Map
val mutableMap = mutableMapOf<String, Any>()
mutableMap["name"] = "Bob"
mutableMap["age"] = 30

四、条件表达式:告别冗余,拥抱表达式思维

Kotlin 的 if 和 when 不仅是语句,更是表达式(可返回值),这是与 Java 最大的区别之一。

1. if 作为表达式:替代三元运算符

Java 中常用 (condition) ? a : b,Kotlin 直接用 if 表达式实现,更灵活:

kotlin 复制代码
val a = 10
val b = 20

// Kotlin 风格:if 表达式直接返回值
val max = if (a > b) a else b

// 多分支 if 表达式(块内最后一行是返回值)
val score = 85
val result = if (score >= 90) {
    "优秀"
} else if (score >= 80) {
    "良好"
} else if (score >= 60) {
    "及格"
} else {
    "不及格"
}
println(result) // 输出:良好

2. when 表达式:强大的模式匹配(替代 switch)

Kotlin 的 when 比 Java 的 switch 强大得多,支持值匹配、类型匹配、范围匹配,甚至无参数模式。

(1)值匹配(替代 switch)

kotlin 复制代码
val day = 3
val dayName = when (day) {
    1 -> "周一"
    2 -> "周二"
    3 -> "周三"
    4 -> "周四"
    5 -> "周五"
    else -> "周末" // 必须有 else(作为表达式时)
}
println(dayName) // 输出:周三

(2)多值匹配与范围匹配

kotlin 复制代码
val score = 75
val level = when (score) {
    in 90..100 -> "优"
    in 80 until 90 -> "良"
    in 60..79 -> "中"
    in 0 until 60 -> "差"
    else -> "无效分数"
}

// 多值合并匹配
val number = 5
val type = when (number) {
    0, 2, 4, 6, 8 -> "偶数"
    1, 3, 5, 7, 9 -> "奇数"
    else -> "未知"
}

(3)类型匹配(智能转换)

kotlin 复制代码
fun processValue(value: Any) {
    when (value) {
        is String -> println("字符串长度:${value.length}") // 智能转换为 String
        is Int -> println("整数平方:${value * value}")    // 智能转换为 Int
        is List<*> -> println("列表大小:${value.size}")
        else -> println("未知类型")
    }
}
processValue("Kotlin") // 输出:字符串长度:6
processValue(5)        // 输出:整数平方:25

(4)无参数 when(替代 if-else 链)

当判断条件不是同一个值时,可省略 when 的参数:

kotlin 复制代码
val age = 25
val hasLicense = true
val canDrive = when {
    age < 18 -> "未成年,不能开车"
    !hasLicense -> "成年但无驾照"
    else -> "可以开车"
}
println(canDrive) // 输出:可以开车

⚠️ 新手必踩坑点 ①:when 表达式遗漏分支,导致逻辑异常

kotlin 复制代码
// 错误示例
fun getLevel(score: Int): String {
    return when (score) {
        in 90..100 -> "优"
        in 80..89 -> "良"
        in 60..79 -> "中"
        // 遗漏了 else 分支!
    }
}
// 编译报错:'when' expression must be exhaustive

正确写法:when 作为表达式时,必须覆盖所有可能的情况(或加 else 分支)。

kotlin 复制代码
fun getLevel(score: Int): String {
    return when (score) {
        in 90..100 -> "优"
        in 80..89 -> "良"
        in 60..79 -> "中"
        else -> "差" // 覆盖剩余情况
    }
}

五、循环遍历:for-in、forEach 与区间的灵活运用

Kotlin 提供了多种循环方式,结合区间(Range)和高阶函数,遍历逻辑更简洁。

1. for-in 循环:遍历集合与区间

kotlin 复制代码
// 遍历 List
val fruits = listOf("Apple", "Banana", "Orange")
for (fruit in fruits) {
    println(fruit)
}

// 遍历索引(withIndex)
for ((index, fruit) in fruits.withIndex()) {
    println("索引 $index:$fruit")
}

// 遍历 Map(解构声明)
val userMap = mapOf("name" to "Alice", "age" to 25)
for ((key, value) in userMap) {
    println("$key -> $value")
}

2. 区间(Range):...、until、downTo、step

Kotlin 用区间表示连续的数值范围,是循环的利器:

写法 含义 示例输出
1...5 闭区间 [1, 5] 1 2 3 4 5
1 until 5 左闭右开 [1, 5) 1 2 3 4
5 downTo 1 降序闭区间 [5, 1] 5 4 3 2 1
1...10 step 2 带步长 1 3 5 7 9
kotlin 复制代码
// 闭区间
for (i in 1..5) print("$i ") // 输出:1 2 3 4 5

// 半开区间
for (i in 1 until 5) print("$i ") // 输出:1 2 3 4

// 倒序区间
for (i in 5 downTo 1) print("$i ") // 输出:5 4 3 2 1

// 步长为 2
for (i in 1..10 step 2) print("$i ") // 输出:1 3 5 7 9

3. forEach 高阶函数:函数式遍历

forEach 是集合的扩展函数,结合 Lambda 表达式,遍历更简洁:

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

// 普通 forEach
numbers.forEach { println(it) } // it 是默认参数名

// 带索引的 forEachIndexed
numbers.forEachIndexed { index, num ->
    println("索引 $index:$num")
}

// Map 的 forEach
val map = mapOf("a" to 1, "b" to 2)
map.forEach { (key, value) -> println("$key -> $value") }

六、集合高阶函数:filter/map/find/sortedBy 链式调用实战

Kotlin 集合的核心优势在于高阶函数,通过 filter、map、find 等函数的链式调用,能以极少的代码完成复杂的数据处理。

1. 常用高阶函数详解

函数名 作用 返回值类型
filter 过滤符合条件的元素 List/Set
map 对每个元素进行转换 List
find / firstOrNull 查找第一个符合条件的元素(找不到返回 null) T?
sortedBy 按指定字段排序(升序) List
sortedByDescending 按指定字段排序(降序) List
groupBy 按条件分组,返回 Map Map
any 判断是否至少有一个元素符合条件 Boolean
all 判断是否所有元素都符合条件 Boolean
count 统计满足条件的元素个数 Int
maxByOrNull 按条件查找最大值对应的元素 T?

2. 链式调用实战:处理学生数据

先定义一个数据类 Student:

kotlin 复制代码
data class Student(
    val name: String,
    val score: Int,
    val gender: String
)

创建学生列表并进行链式处理:

kotlin 复制代码
val students = listOf(
    Student("Alice", 85, "女"),
    Student("Bob", 58, "男"),
    Student("Charlie", 92, "男"),
    Student("Diana", 76, "女"),
    Student("Eric", 60, "男")
)

// 需求:找出所有及格(>=60)的男生,按分数降序排列,提取姓名列表
val result = students
    .filter { it.score >= 60 }           // 过滤及格
    .filter { it.gender == "男" }        // 过滤男生
    .sortedByDescending { it.score }     // 按分数降序
    .map { it.name }                     // 提取姓名

println(result) // 输出:[Charlie, Eric]

// 其他常用操作
val avgScore = students.filter { it.score >= 60 }.map { it.score }.average()
val topStudent = students.maxByOrNull { it.score }
val hasFailed = students.any { it.score < 60 }

println("及格平均分:$avgScore")
println("最高分学生:${topStudent?.name}")
println("是否有不及格:$hasFailed")

七、新手必踩坑点清单

坑点 ①:向不可变集合添加元素,触发编译报错

错误示例:

kotlin 复制代码
val list = listOf(1, 2, 3)
list.add(4) // 编译报错:Unresolved reference 'add'

原因:listOf() 返回的是 List 接口(只读),没有 add 方法。

正确写法

kotlin 复制代码
// 用 mutableListOf 创建可变集合
val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4) // 正常执行

// 或通过 toMutableList() 转换
val list = listOf(1, 2, 3)
val newList = list.toMutableList()
newList.add(4)

坑点 ②:when 表达式遗漏分支,导致编译报错或逻辑异常

上文已详述,此处不再赘述。记住一句话:when 作为表达式时,必须穷尽所有可能,或添加 else 兜底


八、综合小案例:学生成绩管理器控制台程序

让我们把今天学的所有知识点串联起来,开发一个功能完整的学生成绩管理器。

需求说明

  • 定义学生列表(包含姓名、成绩)
  • 过滤及格分数(≥60)
  • 计算及格学生的平均分
  • 对每个学生评级(优/良/中/差)
  • 找出并打印最高分学生

完整代码实现

kotlin 复制代码
// 1. 定义学生数据类
data class Student(
    val name: String,
    val score: Int
)

fun main() {
    // 2. 创建学生列表
    val students = listOf(
        Student("Alice", 95),
        Student("Bob", 55),
        Student("Charlie", 82),
        Student("Diana", 73),
        Student("Eric", 60),
        Student("Fiona", 45)
    )

    println("========== 学生成绩管理器 ==========\n")

    // 3. 过滤及格学生
    val passedStudents = students.filter { it.score >= 60 }
    println("✅ 及格学生名单(共 ${passedStudents.count()} 人):")
    passedStudents.forEach { println("   ${it.name}:${it.score}分") }

    // 4. 计算及格平均分
    val avgScore = passedStudents.map { it.score }.average()
    println("\n📊 及格平均分:${
        String.format("%.2f", avgScore)
    }")

    // 5. 学生评级
    println("\n🏆 学生成绩评级:")
    students.forEach { student ->
        val level = when (student.score) {
            in 90..100 -> "优"
            in 80 until 90 -> "良"
            in 60..79 -> "中"
            else -> "差"
        }
        println("   ${student.name} (${student.score}分) -> $level")
    }

    // 6. 找出最高分学生
    val topStudent = students.maxByOrNull { it.score }
    topStudent?.let {
        println("\n🥇 最高分得主:${it.name},分数:${it.score}分")
    }

    // 7. 额外:统计各等级人数
    val gradeCount = students.groupingBy { student ->
        when (student.score) {
            in 90..100 -> "优"
            in 80 until 90 -> "良"
            in 60..79 -> "中"
            else -> "差"
        }
    }.eachCount()
    
    println("\n📈 各等级人数统计:")
    gradeCount.forEach { (grade, count) ->
        println("   $grade : $count 人")
    }

    println("\n=====================================")
}

运行结果预览

text 复制代码
========== 学生成绩管理器 ==========

✅ 及格学生名单(共 4 人):
   Alice:95分
   Charlie:82分
   Diana:73分
   Eric:60分

📊 及格平均分:77.50

🏆 学生成绩评级:
   Alice (95分) -> 优
   Bob (55分) -> 差
   Charlie (82分) -> 良
   Diana (73分) -> 中
   Eric (60分) -> 中
   Fiona (45分) -> 差

🥇 最高分得主:Alice,分数:95分

📈 各等级人数统计:
   优 : 1 人
   差 : 2 人
   良 : 1 人
   中 : 2 人

=====================================

案例技术点总结

  • filter 过滤及格学生
  • map 提取分数并调用 average() 计算均值
  • when 表达式进行分数评级
  • maxByOrNull 安全地查找最高分
  • groupingBy 配合 eachCount 进行分组统计
  • 全程使用安全调用与 Elvis 运算符,无 !!

九、总结与下篇预告

恭喜你又啃下了一块硬骨头!今天我们系统学习了 Kotlin 进阶的核心技能:

  • ✅ Kotlin 集合框架的 5 大核心优势:可变性控制、空安全、高阶函数、极简语法、Java 互操作
  • ✅ 集合创建:分清 listOf 和 mutableListOf,默认优先不可变
  • ✅ 条件表达式:if 和 when 都能作为表达式直接返回值,when 功能尤其强大
  • ✅ 区间与循环:...、until、downTo、step,遍历集合花样百出
  • ✅ 高阶函数链式调用:filter、map、sortedBy 让你的代码从"怎么做"变成"做什么"
  • ✅ 避坑指南:不可变集合无法修改、when 表达式必须穷尽分支

这些知识是后续学习 Android UI 开发、数据展示、网络请求处理的基石。尤其是集合高阶函数,在实际开发中的使用频率极高,一定要多敲代码,形成肌肉记忆。

💡 学习建议:尝试把今天案例中的学生数据换成你自己的测试数据,自己动手改写一下链式调用的顺序,看看效果有什么不同。编程最好的老师就是亲自试错。

✨ 如果觉得本文对你有帮助,欢迎点赞、收藏、分享给一起学 Android 的小伙伴!

下一篇我们将正式踏入 Android 世界的大门,讲解 Android 项目结构与第一个界面开发,手把手带你做出第一个能跑在手机上的 App!不见不散!

相关推荐
angerdream18 小时前
Android手把手编写儿童手机远程监控App之广播开机自启动
android·android studio
我命由我123451 天前
Android 开发问题:无法从存储库 “D:\keys\MyNotifications.jks“ 中读取密钥 MyNotifications.
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
BoomHe1 天前
Android (AAOS) 13 编译中间产物(Wifi Jar)
android·android studio·android jetpack
黄林晴1 天前
重磅!Android Studio Quail 1 来了,IDE直接内置 LeakCanary
android studio
billy_huang2 天前
Capacitor的基本使用
javascript·android studio
我命由我123452 天前
Android 开发,getSystemService 警告信息:Must be one of: Context. POWER_SERVICE ...
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
萝卜大战僵尸2 天前
Android Studio_lx
android·ide·android studio
花花鱼2 天前
Android studio CMake4.1 找不到了的解决
android·ide·android studio
Carson带你学Android3 天前
告别 IDE?Android CLI 来了,开发进入 AI Agent 时代
android studio·ai编程