Kotlin杂学:集合的学习之路
1. 集合体系架构1.1 继承层级结构Kotlin 的集合体系建立在Iterable接口之上。Iterable: 迭代器接口支持for循环遍历。Collection: 基础集合接口定义了size、isEmpty()、contains()等只读属性。List/Set: 继承自Collection分别代表有序列表和无序去重集。Map: 独立体系存储键值对。1.2 与 Java 集合的设计哲学对比Java: 所有的集合都是可变的Mutable安全性全靠开发者的自觉或Collections.unmodifiableList。Kotlin: 将接口拆分为只读Read-only和可变Mutable。例如List接口只有get没有add。想修改必须使用MutableList。1.3 核心接口定义Kotlin 通过接口层级实现了“读写分离”Listout T: 只读列表。MutableListT: 继承自List和MutableCollection增加了add/remove。2. 不可变与可变集合2.1 2.2 概念与特性只读集合: 并不代表底层数据不可变而是当前引用没有修改权限。可变集合: 拥有完整的增删改查权限。2.3 创建方式与代码实例// 1. listOf: 创建只读列表 (底层通常是 Arrays.ArrayList) val readOnlyList listOf(Kotlin, Java) // 2. mutableListOf: 创建可变列表 (底层是 ArrayList) val mutableList mutableListOf(Apple) mutableList.add(Banana) // 3. buildList (Kotlin 1.4): 在作用域内可变返回后不可变 val constructedList buildList { add(A) add(B) if (true) add(C) } // 此时 constructedList 是只读的 ListString2.4 底层实现与内存模型在 JVM 上listOf(1, 2)编译后会生成类似Arrays.asList的对象它比普通的ArrayList更轻量因为它不需要预留扩容空间。3. List 深度解析3.1 访问操作val list listOf(A, B, C) // 安全取值避免 IndexOutOfBoundsException val item list.getOrNull(5) ?: None // 索引越界返回 null通过 ?: 给默认值 // 语义化取值 val firstItem list.first() // 第一个 val lastItem list.lastOrNull { it D } // 最后一个符合条件的没有则返回 null3.3 子列表操作val numbers (0..10).toList() // slice: 使用区间截取 val sliced numbers.slice(1..3) // [1, 2, 3] // take/drop: 获取或丢弃 val firstThree numbers.take(3) // [0, 1, 2] val lastSeven numbers.drop(3) // [3, 4, 5, 6, 7, 8, 9, 10]4. Set 深度解析4.1 4.2 实现类与去重HashSet: 无序查询极快O(1)。LinkedHashSet:Kotlin 默认setOf保留插入顺序。4.3 集合运算val a setOf(1, 2, 3) val b setOf(3, 4, 5) println(a intersect b) // [3] 交集 println(a union b) // [1, 2, 3, 4, 5] 并集 println(a - b) // [1, 2] 差集 (A中有B中没有)4.5 去重技巧val users listOf(apple, apply, banana) // distinctBy: 根据特征去重 val result users.distinctBy { it.length } // 长度相同的只保留第一个 - [apple, banana]5. Map 深度解析5.2 访问操作 (核心推荐)val scores mutableMapOf(Math to 90) // getOrPut: 缓存模式的王者 // 如果有 key 就返回没有就执行 Lambda 存入并返回结果 val englishScore scores.getOrPut(English) { 85 } // getValue: 如果 Key 不存在直接抛异常比 get()!! 更安全地表达“这里必须有值” val math scores.getValue(Math)5.4 遍历方式// 利用解构声明遍历代码极其整洁 for ((subject, score) in scores) { println($subject 成绩是: $score) }6. 集合操作符详解 (博客灵魂)6.2 映射操作val data listOf(1, 2, 3, abc) // mapNotNull: 变换的同时过滤掉结果为 null 的项 val ints data.mapNotNull { it.toIntOrNull() } // [1, 2, 3] // flatMap: 1对多变换并铺平 val chars listOf(AB, CD).flatMap { it.toList() } // [A, B, C, D]6.4 分组与聚合val words listOf(Apple, Apricot, Banana) val groups words.groupBy { it.first() } // {A[Apple, Apricot], B[Banana]} // fold: 带初始值的累加 val sum listOf(1, 2, 3).fold(10) { acc, i - acc i } // 10123 166.7 分块与窗口val nums (1..5).toList() println(nums.chunked(2)) // [[1, 2], [3, 4], [5]] 分组 println(nums.windowed(2)) // [[1, 2], [2, 3], [3, 4], [4, 5]] 滑动窗口7. 序列 (Sequence) 性能优化7.1 概念对比Iterable (急切):filter生成一个 Listmap又生成一个 List。大数据量时极度浪费内存。Sequence (惰性): 元素一个一个走完所有流程。// 只有执行 toList() 时逻辑才会真正运行 val sequenceResult hugeList.asSequence() .filter { it % 2 0 } .map { it * 2 } .take(10) .toList()8. 与 Java 互操作8.3 平台类型陷阱当 Java 返回一个ListString时Kotlin 会将其视作ListString!平台类型。风险: 这个 List 可能是 null里面的元素也可能是 null。对策: 显式声明类型如val list: ListString javaService.getList()。9. 源码解析 (进阶加分项)9.2 map 操作符源码逐行解析public inline fun T, R IterableT.map(transform: (T) - R): ListR { // 1. 根据当前集合大小预设目标 ArrayList 的容量优化性能 return mapTo(ArrayListR(collectionSizeOrDefault(10)), transform) } public inline fun T, R, C : MutableCollectionin R IterableT.mapTo(destination: C, transform: (T) - R): C { for (item in this) { // 2. 核心就是一个简单的 for 循环 destination.add(transform(item)) } return destination }源码心得: Kotlin 的集合操作符之所以性能好是因为它大量使用了inline内联。这意味着你的 Lambda 表达式在编译后并不会产生额外的函数调用开销。总结Kotlin 集合并不是对 Java 集合的重写而是一套精妙的扩展函数库。它通过读写分离的接口解决了安全问题通过内联函数解决了性能问题通过丰富的操作符解决了开发效率问题。