1. 概述
本文将深入讲解如何在 Kotlin 中处理多维数组。我们会先简要回顾 Kotlin 的 Array
类型,然后重点介绍多维数组的创建与访问方式。
对于有经验的开发者来说,数组是再熟悉不过的基础结构。但在 Kotlin 中,由于其独特的类型系统和语法糖,多维数组的使用方式与 Java 或其他语言略有不同,稍不注意就容易踩坑✅。掌握这些细节,能让你在图像处理、矩阵运算等场景中更加得心应手。
2. 一维数组基础
Kotlin 中的 数组 是一种内置的泛型类 Array<T>
,用于存储相同类型的元素集合。我们通常通过构造函数来初始化数组,该构造函数接收两个参数:数组大小和一个初始化 lambda 表达式。
public class Array<T> {
//...
public inline constructor(size: Int, init: (Int) -> T)
}
其中 T
是泛型类型参数,允许我们创建任意类型的数组。举个例子,假设我们有一个表示像素的 data class:
data class Pixel(val red: Int, val green: Int, val blue: Int)
val pixelArray: Array<Pixel> = Array(10) { Pixel(
(0..255).random(),
(0..255).random(),
(0..255).random())
}
上面代码创建了一个包含 10 个随机颜色像素的一维数组。每个 RGB 分量都在 0~255 范围内,组合起来可生成约 1677 万种颜色。
访问数组元素可以直接使用下标语法(即 []
),这是 Kotlin 对 get()
和 set()
方法的重载:
for(i in 0..9){
print(pixelArray[i]) // 输出类似 "Pixel(red=123, green=45, blue=67)"
Assertions.assertThat((pixelArray[i].red in 0..255))
Assertions.assertThat((pixelArray[i].blue in 0..255))
Assertions.assertThat((pixelArray[i].green in 0..255))
}
可以把这个一维数组想象成一条由 10 个彩色像素组成的水平线:
3. 多维数组构建与使用
要在 Kotlin 中创建二维或更高维度的数组,核心思路是:用数组嵌套数组。也就是说,二维数组的本质是一个数组,其每个元素又是另一个数组。
比如我们要创建一个 10×10 的像素矩阵(可以理解为一张小图片):
val matrix: Array<Array<Pixel>> = Array(10) {
Array(10) {
Pixel(
(0..255).random(),
(0..255).random(),
(0..255).random()
)
}
}
✅ 这里外层 Array(10)
创建了 10 行,每行由内层 Array(10)
构造出一个包含 10 个像素的数组。
遍历并打印所有像素值:
for (i in 0..9) {
for (j in 0..9) {
print(matrix[i][j])
Assertions.assertThat((matrix[i][j].red in 0..255))
Assertions.assertThat((matrix[i][j].blue in 0..255))
Assertions.assertThat((matrix[i][j].green in 0..255))
}
println()
}
通过 matrix[i][j]
即可访问第 i 行 j 列的像素,相当于二维坐标 (x=i, y=j)。
可视化结构如下:
扩展到三维也是一样的逻辑——数组套数组再套数组:
val video: Array<Array<Array<Pixel>>> = Array(10) {
Array(10) {
Array(10) {
Pixel(
(0..255).random(),
(0..255).random(),
(0..255).random()
)
}
}
}
for (i in 0..9) {
for (j in 0..9) {
for(k in 0..9) {
print(video[i][j][k])
Assertions.assertThat((video[i][j][k].red in 0..255))
Assertions.assertThat((video[i][j][k].blue in 0..255))
Assertions.assertThat((video[i][j][k].green in 0..255))
}
println()
}
println("---")
}
⚠️ 注意三层嵌套带来的性能开销。这种结构适合模拟“随时间变化的图像”——X/Y 是空间维度,Z 可视为帧序列的时间轴。
4. 基本类型多维数组优化
当处理基本数据类型(如 Int
, Boolean
等)时,直接使用 Array<Array<Int>>
会导致装箱/拆箱开销,影响性能❌。
Kotlin 提供了专用的原生数组类型来避免这个问题:
IntArray
→int[]
(JVM 层面对应)DoubleArray
BooleanArray
LongArray
等
我们可以结合 arrayOf()
和 intArrayOf()
快速构建高效的二维整型数组:
val first: Array<IntArray> = arrayOf(
intArrayOf(2, 4, 6), // 第一行三个元素
intArrayOf(1, 3, 5) // 第二行三个元素
)
✅ 这样每一行都是真正的 int[]
,没有对象包装,内存更紧凑,访问更快。
如果需要创建非规则矩阵(各行长度不同),可以用 arrayOfNulls
配合手动初始化:
val arrayNulls = arrayOfNulls<Array<Int>>(2)
arrayNulls[0] = Array(3) { 0 }
arrayNulls[1] = Array(2) { 1 }
最终得到一个两行的不规则数组:
- 第一行:
[0, 0, 0]
- 第二行:
[1, 1]
⚠️ 注意此时仍是 Array<Int>
的数组,若追求极致性能建议统一使用 IntArray
替代 Array<Int>
。
5. 使用 Multik 库简化操作
对于科学计算、机器学习等涉及大量多维数组运算的场景,推荐使用 JetBrains 官方推出的 Multik 库。它提供了类似 NumPy 的 API,极大简化了张量操作。
Gradle 依赖配置
implementation "org.jetbrains.kotlinx:multik-core:0.2.2"
implementation "org.jetbrains.kotlinx:multik-default:0.2.2"
Maven 依赖配置
<dependency>
<artifactId>multik-core</artifactId>
<groupId>org.jetbrains.kotlinx</groupId>
<version>0.2.2</version>
</dependency>
<dependency>
<artifactId>multik-core-jvm</artifactId>
<groupId>org.jetbrains.kotlinx</groupId>
<version>0.2.2</version>
</dependency>
<dependency>
<artifactId>multik-default</artifactId>
<groupId>org.jetbrains.kotlinx</groupId>
<version>0.2.2</version>
</dependency>
<dependency>
<artifactId>multik-default-jvm</artifactId>
<groupId>org.jetbrains.kotlinx</groupId>
<version>0.2.2</version>
</dependency>
✅ Multik 支持自动广播、切片、数学运算等高级功能,在处理大规模数据时比原生数组方便得多。例如创建一个 3×3 的浮点矩阵并求转置,几行代码即可完成。
不过对于简单业务逻辑,仍建议使用原生数组以减少依赖复杂度。
6. 总结
本文系统梳理了 Kotlin 中多维数组的实现方式:
- ✅ 多维数组本质是数组的嵌套:
Array<Array<T>>
- ✅ 访问使用
arr[i][j][k]
下标语法,直观易懂 - ⚠️ 基本类型优先使用
IntArray
等原生数组避免装箱 - ✅ 不规则数组可用
arrayOfNulls
动态构建 - ✅ 复杂数值计算推荐引入 Multik 库提升开发效率
所有示例代码均已上传至 GitHub:https://github.com/example/kotlin-arrays-demo 可自由参考使用。