// ============================================
// 第 3 课:集合(List / Set / Map)+ 空安全(null)
// ============================================
fun main() {
// ========== 一、List:有序的列表(最常用)==========
// listOf 创建一个“只读”列表(不能增删改)
val fruits = listOf("苹果", "香蕉", "橙子")
println("水果列表:$fruits")
println("一共有 ${fruits.size} 种") // .size 是元素个数
println("第一个是 ${fruits[0]}") // 用 [下标] 取元素,下标从 0 开始!
println("最后一个是 ${fruits.last()}")
// 遍历列表
print("挨个打印:")
for (fruit in fruits) print("$fruit ")
println()
// 想要能“增删改”的列表,用 mutableListOf
val numbers = mutableListOf(1, 2, 3)
numbers.add(4) // 添加
numbers.remove(2) // 删除元素 2
numbers[0] = 100 // 修改第 0 个
println("可变列表操作后:$numbers")
// ========== 二、集合的“函数式”操作(超好用)==========
val scores = listOf(50, 88, 92, 71, 45)
// filter:筛选出满足条件的元素(这里筛及格的)
val passed = scores.filter { it >= 60 } // it 代表“当前这个元素”
println("及格的分数:$passed")
// map:把每个元素“变换”成新的值(这里每个分数 +5)
val bonus = scores.map { it + 5 }
println("每人加5分:$bonus")
// 还能链式组合:先筛及格,再看有几个
println("及格人数:${scores.filter { it >= 60 }.size}")
// 一些常用统计
println("总分=${scores.sum()}, 最高=${scores.max()}, 平均=${scores.average()}")
// ========== 三、Map:键值对(像字典:名字 -> 电话)==========
val phoneBook = mapOf(
"小明" to "138xxxx",
"小红" to "139xxxx"
)
println("小明的电话:${phoneBook["小明"]}") // 用 [键] 取值
// 遍历 Map
for ((name, phone) in phoneBook) {
println("$name 的号码是 $phone")
}
// ========== 四、空安全 null(Kotlin 的招牌特性)==========
// 在很多语言里,变量没值时是 null,一不小心访问就崩溃(著名的“空指针异常”)。
// Kotlin 从语法层面帮你防住这个问题。
// 默认情况下,变量【不允许】为 null:
val city: String = "北京"
// val bad: String = null // ← 这行会直接编译报错,不允许!
// 如果确实可能为空,要在类型后面加 ? 表示“可空”
// 这里从下面的 findNickname 函数拿昵称,它可能返回名字,也可能返回 null
val nickname: String? = findNickname(id = 99) // 传个查不到的 id,会得到 null
// 对可空变量,不能直接用,否则编译器不让过。要用下面几种安全方式:
// (1) 安全调用 ?. —— 如果是 null 就不执行,直接返回 null
println("昵称长度:${nickname?.length}") // nickname 是 null,所以打印 null
// (2) Elvis 运算符 ?: —— “如果左边是 null,就用右边的默认值”
val len = nickname?.length ?: 0
println("昵称长度(为空则算0):$len")
val show = nickname ?: "匿名用户"
println("显示名:$show")
// (3) 先判断再使用(判断后编译器就知道它不是 null 了)
val nickname2 = findNickname(id = 1) // 这次能查到
if (nickname2 != null) {
// 进到这里,编译器已确定 nickname2 不是 null,可以放心用 .length
println("昵称有值:$nickname2,长度 ${nickname2.length}")
} else {
println("昵称是空的")
}
// 综合示范:从通讯录查一个不存在的人
val unknown = phoneBook["路人甲"] // 查不到,返回 null
println("路人甲的电话:${unknown ?: "查无此人"}")
}
// 一个返回“可空类型 String?”的函数:id 为 1 时返回名字,否则返回 null
fun findNickname(id: Int): String? {
return if (id == 1) "小明" else null
}
全部跑通 🎉 结果都对(注意看可变列表那行:[1,2,3] → 加 4、删掉元素 2、把第 0 个改成 100 → 最终 [100, 3, 4]):
水果列表:[苹果, 香蕉, 橙子]
及格的分数:[88, 92, 71]
每人加5分:[55, 93, 97, 76, 50]
总分=346, 最高=92, 平均=69.2
昵称长度:null
显示名:匿名用户
路人甲的电话:查无此人
第 3 课知识点复盘
一、List(列表)—— 有序的一串数据
|
创建只读列表 |
listOf("a","b") |
不能增删改 |
|
创建可变列表 |
mutableListOf(1,2) |
能 add / remove / 改 |
|
取元素 |
list[0] |
下标从 0 开始(第一个是 0 号!) |
|
个数 |
list.size |
|
|
遍历 |
for (x in list) { } |
⚠️ 新手最容易踩的坑:下标从 0 开始。3 个元素的下标是 0、1、2,没有 list[3](会崩溃)。
二、函数式操作 —— Kotlin 的精华 ✨
这是让代码变优雅的关键,日后写业务逻辑天天用:
scores.filter { it >= 60 } // 筛选:留下满足条件的
scores.map { it + 5 } // 变换:把每个元素改成新值
- { … } 是一小段处理逻辑(叫 lambda,后面会专门讲)
- it 是关键字,代表"当前正在处理的这个元素"
- 还能链式连起来:scores.filter{…}.map{…}.sum()
三、Map(映射)—— 键值对,像字典
val m = mapOf("小明" to "138xxxx") // "键" to "值"
m["小明"] // 用键取值
for ((k, v) in m) { } // 同时拿到键和值
四、空安全 —— Kotlin 的招牌 🛡️
这是 Kotlin 相比很多老语言最大的优势之一,专门防止"空指针崩溃"。
|
String |
不可空 |
默认不允许是 null |
|
String? |
可空 |
加 ? 才允许为 null |
|
a?.length |
安全调用 |
a 是 null 就返回 null,不崩溃 |
|
a ?: 默认值 |
Elvis 运算符 |
a 是 null 就用右边的默认值 |
|
if (a != null) {…} |
判空 |
判断后编译器就允许直接用 a |
对照输出理解:
- nickname 是 null → nickname?.length 打印 null(安全调用没崩)
- nickname ?: "匿名用户" → 因为是 null,用了默认值 匿名用户
- nickname2 查到了"小明" → 判空后能安全访问 .length,得到 2
💡 为什么这么重要?在 Java、C 等语言里,几乎所有 App 崩溃的头号原因就是"空指针"。Kotlin 用 ? 从编译阶段就逼你处理好,让 App 更稳定。这也是 Google 推荐用 Kotlin 写安卓的重要原因。
小练习 ✍️
给你个综合题(可以让我帮你跑):
有一个列表 val ages = listOf(15, 22, 17, 30, 8),用 filter 筛选出所有成年人(≥18),并打印出成年人的数量。