数组
数组是一种数据结构,包含固定数量的相同类型或其子类型的值。数组元素是有序的,可以通过索引访问。
Kotlin 提供了 Array<T> 类以及原生类型数组。
何时使用数组
在与 Java API 互操作或有底层需求时使用数组。例如,如果你对性能的要求超出了常规应用的需求,或者需要构建自定义数据结构。
对于大多数用例,请改用集合。
| 功能 | 数组 | 集合 |
|---|---|---|
| 大小 | 固定 | 取决于类型 |
| 只读变体 | 无,始终是可变的 | 是 (List 和 Set) |
| 添加和移除元素 | 无原生支持。 分配并复制到新数组 | 是(可变集合) |
使用 == 检查结构相等 | 否,比较的是引用。 请改用 .contentEquals() | 是 |
| 原生类型值 | 原生类型数组存储值且 无需装箱 | 通常会被装箱 |
| Java 互操作性 | 映射为 T[] | 映射为 java.util.List 和java.util.Set |
| 函数式风格的过滤与 转换 | 有限 | 广泛 |
了解如何将数组转换为集合。
创建数组
要创建数组,你可以使用:
arrayOf()、arrayOfNulls()或emptyArray()函数。Array构造函数。
使用
val声明数组只能防止重新为变量赋值,但不会使内容变为只读。 无论是在val还是var数组中,元素都是可变的。这种区别仅影响引用,不影响内容。 若需只读视图,请改用集合。
带有值的数组
要从一组已知值创建类型化数组,请使用 arrayOf() 函数。Kotlin 会自动推断类型:
fun main() {
val simpleArray = arrayOf(1, 2, 3) // Array<Int>
println(simpleArray.joinToString())
// 1, 2, 3
}空数组
要创建一个不含元素的数组,请使用 emptyArray() 函数。你可以在赋值语句的左侧或右侧指定元素的类型:
val emptyArrayRight = emptyArray<String>()
val emptyArrayLeft: Array<String> = emptyArray()了解如何向数组添加元素。
带有 null 的数组
要创建一个填充了 null 元素且给定大小的数组,请使用 arrayOfNulls() 函数:
fun main() {
val nullArray: Array<Int?> = arrayOfNulls(3)
println(nullArray.joinToString())
// null, null, null
}Array 构造函数
Array 构造函数接收数组大小以及一个返回数组元素值的函数:
fun main() {
val zeroes = Array<Int>(3) { 0 }
println(zeroes.joinToString())
// 0, 0, 0
val squares = Array(5) { i -> i * i }
println(squares.joinToString())
// 0, 1, 4, 9, 16
}嵌套数组
要创建嵌套数组或多维数组,请使用数组的数组。嵌套数组不需要具有相同的类型或相同的大小。
fun main() {
// 创建一个二维数组
val twoDArray = Array(2) { Array<Int>(2) { 0 } }
println(twoDArray.contentDeepToString())
// [[0, 0], [0, 0]]
// 创建一个三维数组
val threeDArray = Array(3) { Array(3) { Array<Int>(3) { 0 } } }
println(threeDArray.contentDeepToString())
// [[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
}原生类型数组
如果在 Array 类中使用原生类型值,编译器会将这些值装箱为对象。为了避免装箱开销,你可以使用专门的原生类型数组。它们不是 Array<T> 类的子类,但提供了类似的函数和属性集。
| Kotlin 类型 | Java 等效项 |
|---|---|
BooleanArray | boolean[] |
ByteArray | byte[] |
CharArray | char[] |
DoubleArray | double[] |
FloatArray | float[] |
IntArray | int[] |
LongArray | long[] |
ShortArray | short[] |
Kotlin 没有专门的
StringArray类型。String不是原生类型,因此请改用带有推断类型的arrayOf()或arrayOf<String>()函数。
要创建原生类型数组,请使用以下选项之一:
构造函数:
kotlinfun main() { // 创建一个大小为 5 且值初始化为零的 Int 数组 val primitiveTypeArray = IntArray(5) println(primitiveTypeArray.joinToString()) // 0, 0, 0, 0, 0 // 创建一个 Int 数组并接收一个初始值设定项函数 val squares = IntArray(5) { i -> i * i } println(squares.joinToString()) // 0, 1, 4, 9, 16 }工厂函数:
kotlinfun main() { // 创建一个包含 5 个元素的 Int 数组 val numbers = intArrayOf(1, 2, 3, 4, 5) println(numbers.joinToString()) // 1, 2, 3, 4, 5 // 创建一个包含 3 个元素的 Char 数组 val characters = charArrayOf('K', 't', 'l') println(characters.joinToString()) // K, t, l // 创建一个包含 3 个元素的 Double 数组 val doubles = doubleArrayOf(0.22, 4.16, 0.5) println(doubles.joinToString()) // 0.22, 4.16, 0.5 }
要将原生类型数组转换为对象类型数组,请使用
.toTypedArray()函数。要将对象类型数组转换为原生类型数组,请使用
.toBooleanArray()、.toByteArray()、.toCharArray()等。
处理数组
数组支持许多与集合相同的操作,包括迭代、搜索、排序和转换。在 Kotlin 中,你可以通过使用数组向函数传递可变数量的参数,或对数组本身执行操作来处理数组。下表列出了最常用的属性和函数:
| 成员 | 返回 |
|---|---|
size | 元素数量 |
indices | 有效索引范围 |
lastIndex | 最后一个有效索引 |
first() 和 last() | 第一个和最后一个元素 |
isEmpty() 和 isNotEmpty() | 如果数组为空或不为空,则返回 true |
contains() | 如果数组包含该元素,则返回 true |
在 API 参考中了解更多关于数组属性和函数的信息。
本节介绍一些最常用的操作。
访问和修改元素
要访问和修改数组中的元素,请使用索引访问运算符 ([]):
fun main() {
val simpleArray = arrayOf(1, 2, 3)
val twoDArray = Array(2) { Array<Int>(2) { 0 } }
// 访问元素并修改它
simpleArray[0] = 10
twoDArray[0][0] = 2
// 打印修改后的元素
println(simpleArray[0])
// 10
println(twoDArray[0][0])
// 2
}如果你尝试访问超出数组边界的索引,Kotlin 会在运行时抛出
ArrayIndexOutOfBoundsException。
你还可以使用 fill(element, fromIndex, toIndex) 函数就地替换某个范围内的元素。fromIndex 是包含的,而 toIndex 是排除的:
fun main() {
val arr = IntArray(3)
println(arr.joinToString())
// 0, 0, 0
arr.fill(1)
println(arr.joinToString())
// 1, 1, 1
arr.fill(0, 0, 2)
println(arr.joinToString())
// 0, 0, 1
}在 Kotlin 中,数组是不变的 (invariant)。这意味着 Array<String> 不是 Array<Any> 的子类型。这可以防止可能的运行时类型故障。要表达协变,请使用 Array<out Any> 类型投影:
fun main() {
fun printArr(arr: Array<out Any>) {
for (item in arr) print("$item, ")
}
printArr(arrayOf("k", "t", "n"))
// k, t, n,
}添加和移除元素
由于数组具有固定大小,因此不支持 .add() 和 .remove() 函数。要执行这些操作,你需要创建一个新数组。为此,你可以使用以下选项之一:
使用
.copyOf函数:kotlinfun main() { var arr = intArrayOf(0, 1, 2) arr = arr.copyOf(arr.size + 1) println(arr.joinToString()) // 0, 1, 2, 0 arr[arr.lastIndex] = 3 println(arr.joinToString()) // 0, 1, 2, 3 }使用
+或+=运算符:kotlinfun main() { var arr = intArrayOf(0, 1, 2) arr += 3 println(arr.joinToString()) // 0, 1, 2, 3 arr = arr + intArrayOf(4, 5) println(arr.joinToString()) // 0, 1, 2, 3, 4, 5 }
如果你需要频繁添加或移除元素,请改用可变集合。
比较数组
要比较两个数组是否以相同的顺序包含相同的元素,请使用 .contentEquals() 和 .contentDeepEquals() 函数:
fun main() {
val simpleArray = arrayOf(1, 2, 3)
val anotherArray = arrayOf(1, 2, 3)
// 比较数组内容
println(simpleArray.contentEquals(anotherArray))
// true
// 使用中缀表示法,在元素更改后比较数组内容
simpleArray[0] = 10
println(simpleArray contentEquals anotherArray)
// false
}不要使用相等 (
==) 和不等 (!=) 运算符来比较数组的内容。这些运算符检查的是分配的变量是否指向同一个对象。详细了解为什么 Kotlin 中的数组表现如此,请参阅我们的博客文章。
转换数组
Kotlin 拥有许多用于转换数组的有用函数。本节重点介绍其中几个。请在我们的 API 参考中查看完整列表。
求和
要返回数组中所有元素的总和,请使用 .sum() 函数:
fun main() {
val sumArray = arrayOf(1, 2, 3)
println(sumArray.sum())
// 6
}
.sum()函数只能用于数字数据类型(如Int)的数组。
排序与乱序
你可以使用 .sort() 函数根据自然顺序对数组中的元素进行排序,或者使用 .shuffle() 函数随机打乱它们:
fun main() {
val simpleArray = arrayOf(1, 2, 3)
// 随机打乱元素
simpleArray.shuffle()
println(simpleArray.joinToString())
// 排序元素
simpleArray.sort()
println(simpleArray.joinToString())
// 1, 2, 3
}若要获取新的排序数组而不修改原数组,请改用 .sortedArray() 函数。
向函数传递可变数量的参数
在 Kotlin 中,你可以通过 vararg 形参向函数传递可变实参数量。这在你预先不知道参数数量时非常有用,例如在格式化消息或创建 SQL 查询时。
要向函数传递包含可变实参数量的数组,请使用 扩展 (spread) 运算符 (*)。扩展运算符将数组的每个元素作为单独的实参传递给所选函数:
fun main() {
val lettersArray = arrayOf("c", "d")
printAllStrings("a", "b", *lettersArray)
// abcd
}
fun printAllStrings(vararg strings: String) {
for (string in strings) {
print(string)
}
}有关更多信息,请参阅可变实参数量 (varargs)。
将数组转换为集合
如果你使用的不同 API 中有的使用数组,有的使用集合,那么你可以将数组转换为集合,反之亦然。为此,请使用 .toList()、.toSet() 和 .toMap() 函数。这些函数会将数组中的内容复制到独立的副本中。它们不会反映随后对数组所做的更改。
转换为 List 或 Set
要将数组转换为 List 或 Set,请使用 .toList() 和 .toSet() 函数:
fun main() {
val simpleArray = arrayOf("a", "b", "c", "c")
// 转换为 Set
println(simpleArray.toSet())
// [a, b, c]
// 转换为 List
println(simpleArray.toList())
// [a, b, c, c]
}除非你完全确定原始数组不会在其他地方被修改或共享,否则请勿使用 .asList() 及相关的 as* 函数。这些函数是对原始数组的包装而非复制。因此,对数组的更改会反映在列表中,反之亦然。
fun main() {
val simpleArray = arrayOf("a", "b", "c")
val list = simpleArray.asList()
simpleArray[0] = "d"
println(list)
// [d, b, c]
}转换为 Map
要将数组转换为 Map,请使用 .toMap() 函数。
只有 Pair<K,V> 数组可以转换为 Map。Pair 实例的第一个值将成为键,第二个值将成为值。如果相同的键出现多次,则使用最新的值。
本示例使用中缀表示法调用 .to 函数来创建 Pair 元组:
fun main() {
val pairArray = arrayOf("apple" to 120, "banana" to 150, "cherry" to 90, "apple" to 140)
// 转换为 Map
// 水果是键,卡路里数是值
// 最新的 "apple" 值会覆盖第一个值
println(pairArray.toMap())
// {apple=140, banana=150, cherry=90}
}下一步?
- 在集合概览中详细了解为什么我们建议在大多数用例中使用集合。
- 了解其他基本类型。
- 如果你是 Java 开发者,请参阅我们的 Java 到 Kotlin 集合迁移指南。
