首页 / Android 入门教程 / 集合与泛型

Android 入门教程

集合与泛型

本教程共 100 篇 · 第 15 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程Kotlin集合ListMap泛型in out

15. 集合与泛型

本节目标:搞清 Kotlin 的 List/Set/Map 怎么用、可变与不可变的区别、泛型函数和泛型类怎么写、in/out 变型是什么,知道 Android 里集合该怎么选。

只读与可变集合

Kotlin 集合分两类,这是跟 Java 最大的区别:

  • 只读集合ListSetMap):只能读,不能增删改。
  • 可变集合MutableListMutableSetMutableMap):继承自只读接口,能增删改。
val list: List<Int> = listOf(1, 2, 3)         // 只读
// list.add(4)                                 // 编译报错

val mutable: MutableList<Int> = mutableListOf(1, 2, 3)
mutable.add(4)                                 // OK
Tip
  • 默认用只读集合。只在确实需要修改时才用 Mutable 版本。不可变集合减少意外修改,并发也更安全。

注意「只读」不等于「不可变」:只读接口背后可能是可变对象,别人通过可变引用改了,你这边读到的也变。要真正不可变,复制一份或用不可变库(如 kotlinx.collections.immutable)。

List

val list = listOf("A", "B", "C")

list.size                  // 3
list[0]                    // A
list.first()               // A
list.last()                // C
list.indexOf("B")          // 1
list.contains("A")         // true
"B" in list                // true,等价写法

val mutable = mutableListOf(1, 2, 3)
mutable.add(4)
mutable.removeAt(0)
mutable[0] = 10

Set

不重复元素的集合:

val set = setOf("A", "B", "A")        // 实际存 A, B
println(set.size)                      // 2

val mutable = mutableSetOf(1, 2, 3)
mutable.add(2)                         // 已存在,不加
mutable.add(4)                         // OK

去重常用 Set:

val list = listOf(1, 2, 2, 3, 3, 3)
val unique = list.toSet()              // {1, 2, 3}
val uniqueList = list.distinct()       // [1, 2, 3],更直接

Map

键值对:

val map = mapOf("a" to 1, "b" to 2)

map["a"]                    // 1,取值
map["c"]                    // null,不存在
map.getOrDefault("c", 0)    // 0
map.containsKey("a")        // true
"a" in map                  // true,检查 key

for ((key, value) in map) {
    println("$key = $value")
}

val mutable = mutableMapOf("x" to 1)
mutable["y"] = 2            // 添加
mutable.remove("x")         // 删除
Note
  • to 是个中缀函数,创建 PairmapOf("a" to 1) 比 Java 的 put 调用清爽。

集合操作符

Kotlin 集合有大量链式操作符,是处理数据的利器。常用的:

val nums = listOf(1, 2, 3, 4, 5, 6)

nums.map { it * 2 }                // [2, 4, 6, 8, 10, 12],变换
nums.filter { it % 2 == 0 }        // [2, 4, 6],过滤
nums.filter { it > 2 }.map { it * 10 }  // [30, 40, 50, 60],链式
nums.sortedDescending()            // [6, 5, 4, 3, 2, 1]
nums.take(3)                       // [1, 2, 3],取前 N 个
nums.drop(3)                       // [4, 5, 6],跳过前 N 个
nums.sum()                         // 21
nums.average()                     // 3.5
nums.maxOrNull()                   // 6
nums.minOrNull()                   // 1
nums.any { it > 5 }                // true,是否有满足条件的
nums.all { it > 0 }                // true,是否全部满足
nums.none { it < 0 }               // true,是否全不满足
nums.find { it > 3 }               // 4,找第一个满足的
nums.count { it % 2 == 0 }         // 3,统计满足条件的个数
nums.groupBy { it % 2 }            // {1=[1,3,5], 0=[2,4,6]},分组
nums.associateBy { "key$it" }      // {key1=1, key2=2, ...},转 Map
nums.chunked(2)                    // [[1,2],[3,4],[5,6]],分块
nums.fold(0) { acc, n -> acc + n } // 21,折叠
nums.reduce { acc, n -> acc + n }  // 21,归约(无初始值)

it 是 Lambda 的隐式参数名,单个参数时可以省略声明。

// 实际场景:把用户列表转成 id -> User 的 map
val users = listOf(User("u1", "张三"), User("u2", "李四"))
val userMap = users.associateBy { it.id }
// {u1=User(u1,张三), u2=User(u2,李四)}

// 按首字母分组
val grouped = users.groupBy { it.name.first() }

集合转换

val list = listOf(1, 2, 3)
list.toSet()                    // 转成 Set
list.toMutableList()            // 转成可变 List
list.toMutableList().add(4)     // 复制后改,不影响原 list

val set = setOf(1, 2, 3)
set.toList()                    // 转成 List

toMutableXxx 是复制一份,改新的不影响旧的。

序列(Sequence)

大数据集用 Sequence 替代 List,能惰性求值、减少中间集合:

val result = (1..1_000_000)
    .asSequence()
    .map { it * 2 }
    .filter { it > 100 }
    .take(5)
    .toList()

普通 List 链式会每步创建中间 List,Sequence 是逐元素流式处理,数据量大时省内存。

Note
  • 数据量小(几百以内)用 List 操作符更简单,Sequence 反而有额外开销。大数据或流式处理才用 Sequence。

泛型函数

泛型让函数能处理多种类型:

fun <T> firstOrNull(list: List<T>): T? {
    return if (list.isEmpty()) null else list[0]
}

val s: String? = firstOrNull(listOf("a", "b"))
val n: Int? = firstOrNull(listOf(1, 2))

<T> 是类型参数,调用时编译器根据实参推断 T 的具体类型。

泛型约束

限制类型参数的范围:

fun <T : Number> sum(list: List<T>): Double {
    return list.sumOf { it.toDouble() }
}

fun <T : Comparable<T>> max(a: T, b: T): T {
    return if (a >= b) a else b
}

<T : Number> 表示 T 必须是 Number 或其子类。

泛型类

class Box<T>(val value: T) {
    fun get(): T = value
}

val intBox = Box(42)
val strBox = Box("hello")

变型:in 与 out

泛型子类型关系是难点,理解了能写出更灵活的 API。

协变(out)

out T 表示这个类型只能产出 T(作为返回值),不能消费。这样 Producer<Sub>Producer<Base> 的子类型:

interface Source<out T> {
    fun next(): T          // 只能返回 T,不能接收 T
}

open class Animal
class Dog : Animal()

val dogs: Source<Dog> = /* ... */
val animals: Source<Animal> = dogs    // OK,因为 Source 是协变的

类比:一筐苹果能当一筐水果用(只读不写时安全)。

逆变(in)

in T 表示只能消费 T(作为参数),不能产出:

interface Consumer<in T> {
    fun consume(item: T)    // 只能接收 T,不能返回 T
}

val animalConsumer: Consumer<Animal> = /* ... */
val dogConsumer: Consumer<Dog> = animalConsumer   // OK

不变(默认)

不加 in/outMutableList<T> 既读又写,子类型关系不成立。MutableList<Dog> 不是 MutableList<Animal>,因为放进 Animal 会污染。

Tip
  • 记住口诀:「生产者用 out(读),消费者用 in(写)」。PECS 原则(Producer Extends, Consumer Super)的 Kotlin 版。

Android 实战

// 通用列表 Adapter 持有 List<T>
abstract class BaseAdapter<T> {
    private val items = mutableListOf<T>()
    fun update(newItems: List<T>) {
        items.clear()
        items.addAll(newItems)
    }
}

// 泛型缓存
class MemoryCache<K, V> {
    private val map = mutableMapOf<K, V>()
    fun put(key: K, value: V) { map[key] = value }
    fun get(key: K): V? = map[key]
}

// Compose 列表
LazyColumn {
    items(users) { user ->           // items 是泛型扩展函数
        UserRow(user)
    }
}

// 网络响应通用包装
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Failure(val error: Throwable) : Result<Nothing>()
}
// Nothing 是所有类型的子类型,所以 Failure 是任意 Result<out T> 的子类型

小结

Kotlin 集合分只读和可变两类,默认用只读。链式操作符(map/filter/sort/groupBy 等)是数据处理利器。泛型函数和类让代码复用,out 协变用于生产者、in 逆变用于消费者。下一节学空安全与类型转换。