介绍
在前一节中,我们学习了数组的基本用法。
数组中可以包含哪些类型的元素?
在 Go 中,数组的元素可以是任何原始类型,例如整数、字符串或自定义类型。
如果数组中的元素是数组会发生什么?
那么我们就有了一个多维数组:

如上图所示,紫色框是我们的原始数组。
紫色框数组中的每个元素都是一个新数组(红色框)。
关键概念:
- 二维数组的定义
- 二维数组的初始化
- 遍历二维数组
- 多维数组的使用
在前一节中,我们学习了数组的基本用法。
数组中可以包含哪些类型的元素?
在 Go 中,数组的元素可以是任何原始类型,例如整数、字符串或自定义类型。
如果数组中的元素是数组会发生什么?
那么我们就有了一个多维数组:
如上图所示,紫色框是我们的原始数组。
紫色框数组中的每个元素都是一个新数组(红色框)。
关键概念:
你还记得我们如何定义一个普通数组吗?
var variableName [elementCount]variableType
那么,我们如何用这种最朴素的方法定义一个二维数组呢?
var variableName [elementCount][elementCount]variableType
唯一的区别是,我们在原来的 [elementCount]
前面添加了另一个 [elementCount]
,形成 [elementCount][elementCount]
的格式。
例如,如果我们想定义一个名为 a
的二维数组,容量为 10*10
,类型为 int
,我们可以使用以下语法:
var a [10][10]int
就像短变量声明一样,我们可以使用简短的定义方法来声明数组,甚至是多维数组。
我们可以使用 :=
来声明数组,包括多维数组。
例如:
a := [10][10]int{}
这样我们就定义了一个大小为 10*10
的整数二维数组。
与一维数组类似,我们可以通过以下方式初始化二维数组:
你还记得如何使用这些方法吗?
我们在前一节中已经学习了如何在一维数组中使用这些方法。如果你忘记了,也没关系。
以下部分将回顾这三种初始化方法,并将其扩展到二维数组。
让我们继续使用前一节中创建的 array.go
文件。如果你还没有保存你的工作,可以按如下方式创建文件:
touch ~/project/array.go
在 array.go
中编写以下代码:
package main
import "fmt"
func main() {
// 自动初始化为 0
var simpleArray [3][3]int
// 使用指定的初始值初始化,缺失的元素使用默认值
var numArray = [3][3]int{{1, 2, 3}, {2, 3, 4}}
// 使用指定的初始值初始化
var cityArray = [2][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
fmt.Println(simpleArray) // [[0 0 0] [0 0 0] [0 0 0]]
fmt.Println(numArray) // [[1 2 3] [2 3 4] [0 0 0]]
fmt.Println(cityArray) // [[London Chengdu] [Paris Boston]]
}
以上代码展示了使用初始化列表初始化二维数组的三种方式。
使用以下命令运行代码:
go run ~/project/array.go
输出如下:
[[0 0 0] [0 0 0] [0 0 0]]
[[1 2 3] [2 3 4] [0 0 0]]
[[London Chengdu] [Paris Boston]]
你可以修改这些值,并回顾一维数组的初始化方法。
在二维数组中,我们可以像一维数组一样使用推断长度的方法来初始化它。
在 array.go
中编写以下代码:
package main
import "fmt"
func main() {
// 自动初始化为 0
var simpleArray [3][3]int
// 使用指定的初始值初始化,缺失的元素使用默认值
var numArray = [...][]int{{1, 2, 3, 3}, {2, 3, 4, 3}, {0}}
// 使用指定的初始值初始化
var cityArray = [...][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
fmt.Println(simpleArray) // [[0 0 0] [0 0 0] [0 0 0]]
fmt.Println(numArray) // [[1 2 3 3] [2 3 4 3] [0]]
fmt.Println(cityArray) // [[London Chengdu] [Paris Boston]]
}
以上代码展示了使用推断长度来初始化二维数组。
go run ~/project/array.go
输出结果与初始化列表方法相同。
[[0 0 0] [0 0 0] [0 0 0]]
[[1 2 3 3] [2 3 4 3] [0]]
[[London Chengdu] [Paris Boston]]
然而,与一维数组不同,在二维数组的推断长度初始化中,...
符号只能存在于第一个方括号中。
例如:
var numArray = [...][]int{{1, 2, 3, 3}, {2, 3, 4, 3}}
这段代码是有效的,但以下两种变体是错误的:
var numArray = [][...]int{{1, 2, 3, 3}, {2, 3, 4, 3}}
var numArray = [...][...]int{{1, 2, 3, 3}, {2, 3, 4, 3}}
此外,让我们比较一下 numArray
和 cityArray
。
我们可以看到,在 cityArray
中,我们指定了二维数组的第二个参数的大小,如下所示:
var cityArray = [...][2]string{{"London", "Chengdu"}, {"Paris", "Boston"}}
这意味着我们在初始化时指定了每个子数组的大小为 2
。
如果在初始化时给定的值不足,将使用数据类型的默认值来填充缺失的元素。
如果给定的值数量超过了指定的大小,则会发生错误。
与一维数组类似,你也可以在二维数组中使用指定索引值进行初始化。过程是相似的。
在 array.go
中编写以下代码:
package main
import "fmt"
func main() {
a := [...][]int{1: {1, 2, 3}, 3: {4, 7, 9}}
fmt.Println(a) // [[] [1 2 3] [] [4 7 9]]
fmt.Printf("Type of array a: %T\n", a) // Type of array a: [4][]int
}
go run ~/project/array.go
输出结果为:
[[] [1 2 3] [] [4 7 9]]
Type of array a: [4][]int
以上代码将数组 a
定义为一个长度不确定的二维数组。它将值 [1 2 3]
赋给索引为 1
的数组,并将值 [4 7 9]
赋给索引为 3
的数组。
在自动推断长度的情况下,数组 a
的类型为 [4][]int
。
我们如何遍历一个二维数组?
在学习一维数组时,我们使用了两种方法来遍历数组:使用 range
关键字和使用索引号。
现在让我们将这两种方法应用到遍历二维数组中。
在 array.go
中编写以下代码:
package main
import "fmt"
func main() {
a := [...][]int{{123, 321, 222}, {404, 501, 503}, {857, 419, 857}}
// 方法 1:使用 range 关键字
fmt.Println("使用 range 关键字遍历二维数组")
for index, value := range a {
for i, j := range value {
fmt.Println(index, i, j)
}
}
// 方法 2:使用索引号
fmt.Println("\n使用索引号遍历二维数组")
for i := 0; i < len(a); i++ {
for j := 0; j < len(a[i]); j++ {
fmt.Println(i, j, a[i][j])
}
}
}
以上代码展示了两种遍历二维数组的方法。
go run ~/project/array.go
输出结果为:
使用 range 关键字遍历二维数组
0 0 123
0 1 321
0 2 222
1 0 404
1 1 501
1 2 503
2 0 857
2 1 419
2 2 857
使用索引号遍历二维数组
0 0 123
0 1 321
0 2 222
1 0 404
1 1 501
1 2 503
2 0 857
2 1 419
2 2 857
虽然两种方法生成的结果相同,但它们在本质上是不同的。这些差异在我们实际使用数组时会特别明显。
在上一节中,我们提到两种数组遍历方法在本质上是不同的。
让我们通过一个小例子来解释这一点。
在 array.go
中编写以下代码:
package main
import "fmt"
func main() {
a := [...][]int{{123, 321, 222}, {404, 501, 503}, {857, 419, 857}}
// 方法 1:使用 range 关键字
fmt.Println("使用 range 关键字遍历二维数组")
for _, value := range a {
for _, j := range value {
fmt.Println(j)
}
}
fmt.Println(a)
// 方法 2:使用索引号
fmt.Println("\n使用索引号遍历二维数组")
for i := 0; i < len(a); i++ {
for j := 0; j < len(a[i]); j++ {
fmt.Println(a[i][j])
a[i][j] = 0
}
}
fmt.Println(a)
}
以上代码展示了使用两种不同的方法将二维数组中的所有值设置为 0
。
go run ~/project/array.go
程序输出为:
使用 range 关键字遍历二维数组
123
321
222
404
501
503
857
419
857
[[123 321 222] [404 501 503] [857 419 857]]
使用索引号遍历二维数组
123
321
222
404
501
503
857
419
857
[[0 0 0] [0 0 0] [0 0 0]]
我们发现,当使用 range
关键字遍历数组时,修改其值没有效果。然而,使用索引号遍历数组后,修改其值是有效的。这是因为当我们使用 range
关键字遍历数组时,循环变量 j
实际上是数组值的副本。修改副本的值不会影响原始数组 a
。然而,使用索引号遍历数组时,修改原始数组的值是有效的。
有时,我们需要使用三维数组甚至四维数组。
扩展到更高维度的数组与将一维数组扩展到二维数组并没有太大区别。
让我们看一个简单的例子:
package main
import "fmt"
func main() {
a := [2][2][2]int{}
for i := 0; i < 2; i++ {
for j := 0; j < 2; j++ {
for k := 0; k < 2; k++ {
a[i][j][k] = 1
}
}
}
fmt.Println(a)
}
以上代码展示了如何定义和使用三维数组。对于四维或更高维度的数组,过程是相同的。
go run ~/project/array.go
输出结果为:
[[[1 1] [1 1]] [[1 1] [1 1]]]
然而,更高维度的数组并不常用。因此,理解概念就足够了。
在本实验中,我们学习了以下内容: