5.4.7 数组
原文链接: https://kotlinlang.org/docs/arrays.html
5.4.7 数组
数组是一种数据结构,用来保存固定数量的、类型相同或为其子类型的值。数组元素是有序的,并通过索引访问。
Kotlin 提供了 Array<T> 类以及基本类型数组。
何时使用数组
在与 Java API 互操作或满足底层需求时使用数组。例如,你的性能要求超出普通应用所需,或者你需要构建自定义数据结构。
在大多数使用场景中,请优先使用集合。
| 功能 | 数组 | 集合 |
| 大小 | 固定 | 取决于类型 |
| 只读变体 | 没有,始终可变 | 有(List 和 Set) |
| 添加和移除元素 | 没有原生支持。
需要分配新数组并复制 | 有(可变集合) |
| 使用 == 进行结构相等性比较 | 不行,比较的是引用。
请改用 .contentEquals() | 可以 |
| 基本类型值 | 基本类型数组直接存储值,
无需装箱 | 通常会被装箱 |
| Java 互操作性 | 映射为 T[] | 映射为 java.util.List 和
java.util.Set |
| 函数式风格的过滤与
转换 | 有限 | 非常丰富 |
了解如何把数组转换为集合。
创建数组
要创建数组,你可以使用:
注意: 用 val 声明数组只是阻止重新赋值该变量,并不会让内容变成只读。无论是 val 数组还是 var 数组,元素都是可变的。这一区别只影响引用,不影响内容。如果需要只读视图,请改用集合。
带值的数组
要从一组已知的值创建带类型的数组,请使用 arrayOf() 函数。Kotlin 会自动推断类型:
1
2
3
4
5
| fun main() {
val simpleArray = arrayOf(1, 2, 3) // Array<Int>
println(simpleArray.joinToString())
// 1, 2, 3
}
|
空数组
要创建不含任何元素的数组,请使用 emptyArray() 函数。你可以在赋值的左侧或右侧指定元素类型:
1
2
| val emptyArrayRight = emptyArray<String>()
val emptyArrayLeft: Array<String> = emptyArray()
|
了解如何向数组添加元素。
带 null 的数组
要创建指定大小、全部填充 null 元素的数组,请使用 arrayOfNulls() 函数:
1
2
3
4
5
| fun main() {
val nullArray: Array<Int?> = arrayOfNulls(3)
println(nullArray.joinToString())
// null, null, null
}
|
数组构造器
Array 构造器接收数组大小,以及一个返回数组元素值的函数:
1
2
3
4
5
6
7
8
9
| 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
}
|
嵌套数组
要创建嵌套数组或多维数组,请使用数组的数组。嵌套数组不必具有相同的类型或相同的大小。
1
2
3
4
5
6
7
8
9
10
11
| fun main() {
// Creates a two-dimensional array
val twoDArray = Array(2) { Array<Int>(2) { 0 } }
println(twoDArray.contentDeepToString())
// [[0, 0], [0, 0]]
// Creates a three-dimensional array
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>() 函数。
要创建基本类型数组,请使用以下方式之一:
1
2
3
4
5
6
7
8
9
10
11
| fun main() {
// Creates an Int array of size 5 with the values initialized to zero
val primitiveTypeArray = IntArray(5)
println(primitiveTypeArray.joinToString())
// 0, 0, 0, 0, 0
// Creates an Int array and takes an initializer function
val squares = IntArray(5) { i -> i * i }
println(squares.joinToString())
// 0, 1, 4, 9, 16
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| fun main() {
// Creates an Int array with 5 elements
val numbers = intArrayOf(1, 2, 3, 4, 5)
println(numbers.joinToString())
// 1, 2, 3, 4, 5
// Creates a Char array with 3 elements
val characters = charArrayOf('K', 't', 'l')
println(characters.joinToString())
// K, t, l
// Creates a Double array with 3 elements
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 参考。
本节介绍其中最常用的一些操作。
访问和修改元素
要访问和修改数组中的元素,请使用索引访问运算符([]):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| fun main() {
val simpleArray = arrayOf(1, 2, 3)
val twoDArray = Array(2) { Array<Int>(2) { 0 } }
// Accesses the element and modifies it
simpleArray[0] = 10
twoDArray[0][0] = 2
// Prints the modified element
println(simpleArray[0])
// 10
println(twoDArray[0][0])
// 2
}
|
注意: 如果你尝试访问超出数组边界的索引,Kotlin 会在运行期抛出 ArrayIndexOutOfBoundsException。
你也可以用 fill(element, fromIndex, toIndex) 函数就地替换某个范围内的元素。fromIndex 是包含的,toIndex 是不包含的:
1
2
3
4
5
6
7
8
9
10
11
12
13
| 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 中,数组是不变的。这意味着 Array<String> 不是 Array<Any> 的子类型。这能防止可能出现的运行期类型错误。要表达协变,请使用 Array<out Any> 类型投影:
1
2
3
4
5
6
7
8
| fun main() {
fun printArr(arr: Array<out Any>) {
for (item in arr) print("$item, ")
}
printArr(arrayOf("k", "t", "n"))
// k, t, n,
}
|
添加和移除元素
由于数组大小固定,它们不支持 .add() 和 .remove() 函数。要执行这些操作,你需要创建一个新数组。为此,你可以使用以下方式之一:
1
2
3
4
5
6
7
8
9
10
11
| fun 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
}
|
1
2
3
4
5
6
7
8
9
10
11
| fun 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() 函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| fun main() {
val simpleArray = arrayOf(1, 2, 3)
val anotherArray = arrayOf(1, 2, 3)
// Compares contents of arrays
println(simpleArray.contentEquals(anotherArray))
// true
// Using infix notation, compares contents of arrays after an element
// is changed
simpleArray[0] = 10
println(simpleArray contentEquals anotherArray)
// false
}
|
警告: 不要用相等(==)和不相等(!=)运算符比较数组的内容。这些运算符检查的是被赋值的变量是否指向同一个对象。想了解 Kotlin 中数组为何如此表现,请参阅我们的博客文章。
Kotlin 提供了许多用于转换数组的实用函数。本节重点介绍其中一部分,完整列表请参阅我们的 API 参考。
求和
要返回数组中所有元素的和,请使用 .sum() 函数:
1
2
3
4
5
| fun main() {
val sumArray = arrayOf(1, 2, 3)
println(sumArray.sum())
// 6
}
|
注意: .sum() 函数只能用于数值数据类型的数组,例如 Int。
排序与打乱
你可以用 .sort() 函数按自然顺序对数组元素排序,或用 .shuffle() 函数随机打乱它们:
1
2
3
4
5
6
7
8
9
10
11
12
| fun main() {
val simpleArray = arrayOf(1, 2, 3)
// Randomly shuffles elements
simpleArray.shuffle()
println(simpleArray.joinToString())
// Sorts elements
simpleArray.sort()
println(simpleArray.joinToString())
// 1, 2, 3
}
|
要获得一个排序后的新数组而不修改原数组,请改用 .sortedArray() 函数。
向函数传入数量可变的实参
在 Kotlin 中,你可以通过 vararg 参数向函数传入数量可变的实参。当你事先不知道实参数量时,这会很有用,例如格式化一条消息或构造一条 SQL 查询时。
要把包含数量可变实参的数组传给函数,请使用展开运算符(*)。展开运算符会把数组的每个元素作为单独实参传给所选函数:
1
2
3
4
5
6
7
8
9
10
11
| fun main() {
val lettersArray = arrayOf("c", "d")
printAllStrings("a", "b", *lettersArray)
// abcd
}
fun printAllStrings(vararg strings: String) {
for (string in strings) {
print(string)
}
}
|
更多信息请参阅数量可变的实参(varargs)。
转换为集合
如果你要在有的接口使用数组、有的接口使用集合之间协作,可以把数组转换为集合,反之亦然。为此请使用 .toList()、.toSet() 和 .toMap() 函数。这些函数会把数组中的内容复制到一份独立副本中,不会反映之后对数组所做的修改。
转换为 List 或 Set
要把数组转换为 List 或 Set,请使用 .toList() 和 .toSet() 函数:
1
2
3
4
5
6
7
8
9
10
11
| fun main() {
val simpleArray = arrayOf("a", "b", "c", "c")
// Converts to a Set
println(simpleArray.toSet())
// [a, b, c]
// Converts to a List
println(simpleArray.toList())
// [a, b, c, c]
}
|
除非你完全确定原数组不会被修改、也不会在其他地方共享,否则不要使用 .asList() 以及相关的 as* 函数。这些函数包装原数组而不是复制它。因此对数组的修改会反映到列表中,反之亦然。
1
2
3
4
5
6
7
8
| 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 元组:
1
2
3
4
5
6
7
8
9
| fun main() {
val pairArray = arrayOf("apple" to 120, "banana" to 150, "cherry" to 90, "apple" to 140)
// Converts to a Map
// Fruits are keys, calorie numbers are values
// The latest "apple" value overwrites the first one
println(pairArray.toMap())
// {apple=140, banana=150, cherry=90}
}
|
接下来做什么?