MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Kotlin列表操作指南

2023-11-183.5k 阅读

Kotlin 列表基础

Kotlin 中的列表是一种有序集合,它允许我们存储多个元素,并且可以根据元素的索引来访问和操作这些元素。列表分为可变列表(MutableList)和不可变列表(List)。

不可变列表

不可变列表一旦创建,其内容就不能被修改。我们可以使用 listOf 函数来创建不可变列表。例如:

val numbers = listOf(1, 2, 3, 4, 5)
println(numbers)

上述代码创建了一个包含整数 1 到 5 的不可变列表,并将其打印出来。

不可变列表的优点在于线程安全,适合在多个线程访问的场景下使用。它保证了数据的一致性,不会因为其他线程的修改而导致数据状态的混乱。

可变列表

可变列表允许我们在创建后添加、删除和修改元素。使用 mutableListOf 函数来创建可变列表。例如:

val mutableNumbers = mutableListOf(1, 2, 3)
mutableNumbers.add(4)
mutableNumbers[2] = 33
println(mutableNumbers)

这里我们创建了一个可变列表 mutableNumbers,初始包含 1、2、3。然后添加了元素 4,并将索引为 2 的元素修改为 33,最后打印列表。

可变列表适用于需要频繁修改数据的场景,比如在程序运行过程中动态添加或删除元素的情况。但在多线程环境下需要注意同步问题,否则可能会出现数据竞争。

访问列表元素

通过索引访问

在 Kotlin 中,我们可以像在其他编程语言中一样,通过索引来访问列表中的元素。索引从 0 开始。例如:

val fruits = listOf("apple", "banana", "cherry")
println(fruits[1])

上述代码会打印出 banana,因为索引 1 对应列表中的第二个元素。

在使用索引访问时需要注意边界检查。如果访问的索引超出了列表的范围,会抛出 IndexOutOfBoundsException。例如:

val fruits = listOf("apple", "banana", "cherry")
try {
    println(fruits[3])
} catch (e: IndexOutOfBoundsException) {
    println("Index out of bounds: $e")
}

这段代码尝试访问索引 3,由于列表只有 3 个元素,索引范围是 0 到 2,所以会捕获到 IndexOutOfBoundsException 并打印错误信息。

使用 get 方法

除了使用方括号语法,还可以使用 get 方法来访问列表元素。例如:

val numbers = listOf(10, 20, 30)
println(numbers.get(2))

get 方法在功能上与方括号语法相同,但在某些情况下,比如在链式调用中,使用 get 方法可能会使代码更易读。

遍历列表

for 循环遍历

使用传统的 for 循环来遍历列表是一种常见的方式。例如:

val animals = listOf("dog", "cat", "bird")
for (animal in animals) {
    println(animal)
}

在这个例子中,for 循环依次将 animals 列表中的每个元素赋值给 animal 变量,并打印出来。

如果需要获取元素的索引,可以使用 withIndex 扩展函数。例如:

val fruits = listOf("apple", "banana", "cherry")
for ((index, fruit) in fruits.withIndex()) {
    println("Index $index: $fruit")
}

withIndex 函数会返回一个包含索引和元素的 Pair,通过解构赋值,我们可以在循环中同时获取索引和元素。

forEach 遍历

forEach 是 Kotlin 提供的一种更简洁的遍历方式,它接受一个函数作为参数,对列表中的每个元素执行该函数。例如:

val numbers = listOf(1, 2, 3, 4)
numbers.forEach { number ->
    println(number * 2)
}

这里 forEachnumbers 列表中的每个元素乘以 2 并打印。如果函数体只有一行代码,可以省略花括号,使代码更加简洁:

val numbers = listOf(1, 2, 3, 4)
numbers.forEach { println(it * 2) }

这里 itforEach 函数闭包中的默认参数名,表示当前遍历到的元素。

forEachIndexed 遍历

forEachIndexed 类似于 forEach,但它会同时提供元素的索引。例如:

val colors = listOf("red", "green", "blue")
colors.forEachIndexed { index, color ->
    println("Index $index: $color")
}

在这个例子中,forEachIndexed 函数的闭包接受索引 index 和元素 color,并打印出每个元素及其索引。

列表操作 - 添加元素

可变列表添加元素

对于可变列表,我们可以使用 add 方法来添加单个元素。add 方法有两种形式,一种是将元素添加到列表末尾,另一种是在指定索引位置插入元素。

添加到末尾:

val mutableList = mutableListOf(1, 2, 3)
mutableList.add(4)
println(mutableList)

上述代码将元素 4 添加到 mutableList 的末尾。

在指定位置插入:

val mutableList = mutableListOf(1, 2, 3)
mutableList.add(1, 10)
println(mutableList)

这里将元素 10 插入到索引 1 的位置,原索引 1 及之后的元素会向后移动。

不可变列表添加元素

不可变列表本身不能被修改,但我们可以通过创建新列表的方式来实现类似添加元素的效果。例如,使用 plus 函数:

val list = listOf(1, 2, 3)
val newList = list.plus(4)
println(newList)

plus 函数会创建一个新的列表,包含原列表的所有元素以及新添加的元素 4。注意,原列表 list 并没有被修改。

我们还可以使用 + 运算符来达到相同的效果:

val list = listOf(1, 2, 3)
val newList = list + 4
println(newList)

列表操作 - 删除元素

可变列表删除元素

在可变列表中,可以使用 remove 方法删除指定元素。例如:

val mutableList = mutableListOf(1, 2, 3, 2)
mutableList.remove(2)
println(mutableList)

上述代码会删除列表中第一次出现的元素 2。如果列表中有多个相同元素,只会删除第一个。

要删除指定索引位置的元素,可以使用 removeAt 方法:

val mutableList = mutableListOf(1, 2, 3)
mutableList.removeAt(1)
println(mutableList)

这里删除了索引为 1 的元素 2。

不可变列表删除元素

不可变列表同样不能直接修改,但可以通过创建新列表来达到删除元素的目的。例如,使用 filter 函数:

val list = listOf(1, 2, 3, 4)
val newList = list.filter { it != 3 }
println(newList)

filter 函数会创建一个新列表,其中包含原列表中满足指定条件的元素。这里的条件是元素不等于 3,所以新列表不包含元素 3。

列表操作 - 修改元素

可变列表修改元素

对于可变列表,可以直接通过索引来修改元素的值。例如:

val mutableList = mutableListOf(1, 2, 3)
mutableList[1] = 22
println(mutableList)

上述代码将索引为 1 的元素从 2 修改为 22。

还可以使用 set 方法来达到相同的效果:

val mutableList = mutableListOf(1, 2, 3)
mutableList.set(1, 22)
println(mutableList)

set 方法接受索引和新值作为参数,将指定索引位置的元素替换为新值。

不可变列表修改元素

不可变列表不能直接修改元素,但可以通过创建新列表来模拟修改操作。例如,先使用 filter 函数过滤掉要修改的元素,再使用 plus 函数添加修改后的元素:

val list = listOf(1, 2, 3)
val newList = list.filter { it != 2 } + 22
println(newList)

这里先过滤掉元素 2,然后添加修改后的元素 22,从而创建了一个新列表。

列表转换

转换为数组

在 Kotlin 中,可以将列表转换为数组。对于不可变列表,可以使用 toArray 函数:

val list = listOf(1, 2, 3)
val array = list.toArray()
println(array.contentToString())

toArray 函数会返回一个包含列表元素的数组。注意,这里返回的是 Array<Any> 类型的数组,如果列表元素类型是确定的,可以使用带类型参数的 toTypedArray 函数。例如:

val list = listOf(1, 2, 3)
val intArray = list.toTypedArray()
println(intArray.contentToString())

对于可变列表,同样可以使用 toArraytoTypedArray 函数进行转换。

转换为其他集合类型

Kotlin 列表还可以转换为其他集合类型,比如 Set。可以使用 toSet 函数将列表转换为集合。例如:

val list = listOf(1, 2, 2, 3)
val set = list.toSet()
println(set)

这里将列表转换为集合后,集合中会去除重复元素,所以 set[1, 2, 3]

也可以将列表转换为 Map。假设列表中的元素是 Pair 类型,可以使用 toMap 函数将其转换为 Map。例如:

val list = listOf(Pair("a", 1), Pair("b", 2))
val map = list.toMap()
println(map)

列表过滤与筛选

使用 filter 函数

filter 函数用于从列表中筛选出满足指定条件的元素,返回一个新的列表。例如,筛选出列表中的偶数:

val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers)

这里的 filter 函数接受一个 lambda 表达式作为参数,该表达式定义了筛选条件,即元素对 2 取余等于 0 为偶数。

使用 filterNot 函数

filterNot 函数与 filter 函数相反,它返回列表中不满足指定条件的元素组成的新列表。例如,筛选出列表中的奇数:

val numbers = listOf(1, 2, 3, 4, 5, 6)
val oddNumbers = numbers.filterNot { it % 2 == 0 }
println(oddNumbers)

使用 filterNotNull 函数

当列表中可能包含 null 值时,可以使用 filterNotNull 函数来过滤掉 null 值,返回一个不包含 null 的新列表。例如:

val nullableList = listOf(1, null, 3, null, 5)
val nonNullList = nullableList.filterNotNull()
println(nonNullList)

这里 filterNotNull 函数会去除 nullableList 中的 null 值,返回 [1, 3, 5]

列表映射与转换

使用 map 函数

map 函数用于将列表中的每个元素按照指定的规则进行转换,返回一个新的列表。例如,将列表中的每个数字乘以 2:

val numbers = listOf(1, 2, 3)
val doubledNumbers = numbers.map { it * 2 }
println(doubledNumbers)

这里 map 函数对 numbers 列表中的每个元素应用 it * 2 的转换规则,返回一个新列表 [2, 4, 6]

使用 flatMap 函数

flatMap 函数先对列表中的每个元素应用一个转换函数,该函数返回一个列表,然后将所有这些返回的列表合并成一个新列表。例如,有一个包含多个列表的列表,将其扁平化:

val nestedList = listOf(listOf(1, 2), listOf(3, 4))
val flatList = nestedList.flatMap { it }
println(flatList)

这里 flatMap 函数将 nestedList 中的每个子列表进行合并,返回 [1, 2, 3, 4]

使用 mapNotNull 函数

mapNotNull 函数与 map 函数类似,但它会过滤掉转换过程中产生的 null 值。例如,将列表中的字符串转换为整数,但有些字符串可能无法转换为有效的整数,使用 mapNotNull 可以过滤掉这些无效转换:

val stringList = listOf("1", "two", "3")
val intList = stringList.mapNotNull { it.toIntOrNull() }
println(intList)

这里 toIntOrNull 函数尝试将字符串转换为整数,如果转换失败返回 nullmapNotNull 函数会过滤掉这些 null 值,返回 [1, 3]

列表聚合操作

使用 reduce 函数

reduce 函数用于对列表中的元素进行聚合操作,它接受一个二元操作函数,将列表中的元素两两结合,最终返回一个单一的结果。例如,计算列表中所有元素的和:

val numbers = listOf(1, 2, 3, 4)
val sum = numbers.reduce { acc, number -> acc + number }
println(sum)

这里 reduce 函数从列表的第一个元素开始,将 acc(初始为第一个元素)与后续元素依次通过 acc + number 的操作进行聚合,最终返回所有元素的和 10。

使用 fold 函数

fold 函数与 reduce 函数类似,但它可以指定一个初始值。例如,计算列表中所有元素的乘积,从 1 开始:

val numbers = listOf(2, 3, 4)
val product = numbers.fold(1) { acc, number -> acc * number }
println(product)

这里 fold 函数从初始值 1 开始,与列表中的元素依次通过 acc * number 的操作进行聚合,返回乘积 24。

使用 sumproduct 等函数

Kotlin 为数字类型的列表提供了一些便捷的聚合函数,如 sum 用于计算列表元素的和,product 用于计算列表元素的乘积。例如:

val numbers = listOf(1, 2, 3, 4)
val sum = numbers.sum()
val product = numbers.product()
println("Sum: $sum, Product: $product")

这些函数简化了常见的聚合操作,不需要手动编写 reducefold 逻辑。

列表排序

自然排序

对于实现了 Comparable 接口的元素类型的列表,可以使用 sorted 函数进行自然排序。例如,对整数列表进行升序排序:

val numbers = listOf(3, 1, 4, 2)
val sortedNumbers = numbers.sorted()
println(sortedNumbers)

这里 sorted 函数会返回一个新的已排序的列表 [1, 2, 3, 4],原列表 numbers 保持不变。

自定义排序

如果需要自定义排序规则,可以使用 sortedWith 函数,并传入一个比较器。例如,对字符串列表按照长度进行排序:

val strings = listOf("apple", "banana", "cherry", "date")
val sortedByLength = strings.sortedWith(compareBy { it.length })
println(sortedByLength)

这里 compareBy 函数创建了一个比较器,按照字符串长度进行比较,sortedWith 函数使用这个比较器对列表进行排序,返回按长度排序后的列表。

对于可变列表,可以使用 sortsortWith 函数直接对列表进行排序,而不是返回一个新列表。例如:

val mutableList = mutableListOf(3, 1, 4, 2)
mutableList.sort()
println(mutableList)

这里 sort 函数直接对 mutableList 进行升序排序。

列表查找与搜索

使用 find 函数

find 函数用于在列表中查找满足指定条件的第一个元素。如果找到则返回该元素,否则返回 null。例如,在列表中查找第一个偶数:

val numbers = listOf(1, 3, 4, 5)
val firstEven = numbers.find { it % 2 == 0 }
println(firstEven)

这里 find 函数返回 4,因为 4 是列表中第一个满足偶数条件的元素。

使用 indexOflastIndexOf 函数

indexOf 函数用于查找指定元素在列表中的第一次出现的索引位置,如果不存在则返回 -1。例如:

val fruits = listOf("apple", "banana", "cherry", "banana")
val index = fruits.indexOf("banana")
println(index)

这里 indexOf 函数返回 1,因为 "banana" 第一次出现在索引 1 的位置。

lastIndexOf 函数与 indexOf 函数类似,但它查找的是指定元素最后一次出现的索引位置。例如:

val fruits = listOf("apple", "banana", "cherry", "banana")
val lastIndex = fruits.lastIndexOf("banana")
println(lastIndex)

这里 lastIndexOf 函数返回 3,因为 "banana" 最后一次出现在索引 3 的位置。

列表分组

使用 groupBy 函数可以根据指定的分组依据将列表中的元素分组。例如,将整数列表按照奇偶性分组:

val numbers = listOf(1, 2, 3, 4, 5)
val groupedNumbers = numbers.groupBy { if (it % 2 == 0) "even" else "odd" }
println(groupedNumbers)

这里 groupBy 函数根据元素是偶数还是奇数进行分组,返回一个 Map,其中键是分组的标识("even" 或 "odd"),值是属于该组的元素列表。

列表的高级操作 - 滑动窗口

使用 windowed 函数可以在列表上创建滑动窗口。例如,对列表中的元素进行相邻两个元素的求和:

val numbers = listOf(1, 2, 3, 4)
val windowedSums = numbers.windowed(2).map { it.sum() }
println(windowedSums)

这里 windowed(2) 创建了大小为 2 的滑动窗口,然后对每个窗口内的元素求和,最终返回 [3, 5, 7]

列表的高级操作 - 拉链操作

使用 zip 函数可以将两个列表按元素位置进行配对。例如,将两个列表中的元素一一对应相乘:

val list1 = listOf(1, 2, 3)
val list2 = listOf(4, 5, 6)
val multiplied = list1.zip(list2).map { (a, b) -> a * b }
println(multiplied)

这里 zip 函数将 list1list2 按位置配对,然后 map 函数对每对元素进行相乘操作,返回 [4, 10, 18]

通过以上对 Kotlin 列表操作的详细介绍,开发者可以更加灵活高效地处理列表数据,根据不同的业务需求选择合适的操作方法,提高代码的质量和性能。无论是简单的元素访问、遍历,还是复杂的聚合、分组等操作,Kotlin 都提供了丰富且易用的函数和方法来满足需求。