// ============================================ // 第 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(列表)—— 有序的一串数据
| 操作 | 写法 | 说明 |
|---|---|---|
创建只读列表 |
| 不能增删改 |
创建可变列表 |
| 能 |
取元素 |
| 下标从 0 开始(第一个是 0 号!) |
个数 |
| |
遍历 |
|
⚠️ 新手最容易踩的坑:下标从 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 相比很多老语言最大的优势之一,专门防止"空指针崩溃"。
| 语法 | 名字 | 作用 |
|---|---|---|
| 不可空 | 默认不允许是 null |
| 可空 | 加 |
| 安全调用 | a 是 null 就返回 null,不崩溃 |
| Elvis 运算符 | 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),并打印出成年人的数量。