Go语言数组定义与多维数组使用
Go语言数组定义基础
在Go语言中,数组是一种固定长度的同类型元素的集合。它是值类型,这意味着当将一个数组赋值给另一个数组时,实际上是进行了一次完整的复制操作。数组的定义语法如下:
var 数组名 [长度]数据类型
例如,定义一个长度为5的整数数组:
var numbers [5]int
这里numbers
就是数组名,[5]
表示数组的长度为5,int
表示数组中元素的数据类型为整数。在Go语言中,数组的长度必须是一个常量表达式,并且在编译时就确定。
初始化数组
- 部分初始化 可以只初始化数组的部分元素,未初始化的元素将被赋予其数据类型的零值。例如:
package main
import "fmt"
func main() {
var numbers [5]int
numbers[0] = 10
numbers[2] = 30
fmt.Println(numbers)
}
在上述代码中,我们只初始化了numbers
数组的第0个和第2个元素,其他元素会被自动初始化为0,运行结果为[10 0 30 0 0]
。
- 完全初始化 可以在定义数组时一次性给出所有元素的值,Go语言会根据给出的值的数量自动确定数组的长度。例如:
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
fmt.Println(numbers)
}
这里使用了短变量声明:=
,并且在大括号内给出了5个整数值,数组numbers
的长度被自动确定为5,运行结果为[10 20 30 40 50]
。
另外,如果不想显式指定数组的长度,可以使用...
来让Go语言自动推断数组的长度。例如:
package main
import "fmt"
func main() {
numbers := [...]int{10, 20, 30, 40, 50}
fmt.Println(numbers)
}
上述代码中,...
代替了具体的长度,Go语言会根据大括号内元素的个数确定数组长度为5,运行结果同样为[10 20 30 40 50]
。
访问数组元素
数组元素可以通过索引来访问,索引从0开始,到数组长度减1结束。例如,对于上述定义的numbers
数组,访问第3个元素(索引为2)的代码如下:
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
fmt.Println(numbers[2])
}
运行上述代码,会输出30
。
同时,数组元素也可以通过索引进行赋值操作。例如:
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
numbers[3] = 45
fmt.Println(numbers)
}
运行结果为[10 20 30 45 50]
,可以看到第4个元素(索引为3)的值被成功修改为45。
遍历数组
- 使用for循环遍历
最常见的遍历数组的方式是使用
for
循环。例如:
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
for i := 0; i < len(numbers); i++ {
fmt.Printf("Index %d, Value %d\n", i, numbers[i])
}
}
在上述代码中,len(numbers)
用于获取数组numbers
的长度,通过for
循环从0到len(numbers)-1
遍历数组,输出每个元素的索引和值。
- 使用for - range遍历
Go语言提供了一种更简洁的遍历方式,即
for - range
。例如:
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
for index, value := range numbers {
fmt.Printf("Index %d, Value %d\n", index, value)
}
}
for - range
会同时返回元素的索引和值。如果只需要值而不需要索引,可以使用下划线_
来忽略索引。例如:
package main
import "fmt"
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
for _, value := range numbers {
fmt.Printf("Value %d\n", value)
}
}
数组作为函数参数
在Go语言中,数组作为函数参数传递时,传递的是整个数组的副本。这意味着在函数内部对数组的修改不会影响到函数外部的数组。例如:
package main
import "fmt"
func modifyArray(arr [5]int) {
arr[0] = 100
fmt.Println("Inside function:", arr)
}
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
modifyArray(numbers)
fmt.Println("Outside function:", numbers)
}
运行上述代码,输出为:
Inside function: [100 20 30 40 50]
Outside function: [10 20 30 40 50]
可以看到,在modifyArray
函数内部修改了数组的第一个元素,但函数外部的数组并没有受到影响。
如果希望在函数内部修改数组并影响到函数外部,可以传递数组的指针。例如:
package main
import "fmt"
func modifyArrayPtr(arr *[5]int) {
(*arr)[0] = 100
fmt.Println("Inside function:", *arr)
}
func main() {
numbers := [5]int{10, 20, 30, 40, 50}
modifyArrayPtr(&numbers)
fmt.Println("Outside function:", numbers)
}
运行结果为:
Inside function: [100 20 30 40 50]
Outside function: [100 20 30 40 50]
这里通过传递数组指针&numbers
,在modifyArrayPtr
函数内部通过解引用指针(*arr)
来修改数组元素,从而影响到了函数外部的数组。
Go语言多维数组
多维数组是数组的数组,即在Go语言中可以定义二维数组、三维数组等。以二维数组为例,其定义语法如下:
var 数组名 [第一维长度][第二维长度]数据类型
例如,定义一个二维整数数组:
var matrix [3][4]int
这里matrix
是二维数组名,[3]
表示第一维长度为3,[4]
表示第二维长度为4,即这个二维数组有3行4列。
初始化二维数组
- 部分初始化 可以只初始化部分元素,未初始化的元素将被赋予其数据类型的零值。例如:
package main
import "fmt"
func main() {
var matrix [3][4]int
matrix[0][0] = 10
matrix[1][2] = 30
fmt.Println(matrix)
}
在上述代码中,只初始化了matrix
数组的(0, 0)
和(1, 2)
位置的元素,其他元素会被自动初始化为0,运行结果类似[[10 0 0 0] [0 0 30 0] [0 0 0 0]]
。
- 完全初始化 可以在定义时一次性给出所有元素的值。例如:
package main
import "fmt"
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
fmt.Println(matrix)
}
这里使用了大括号嵌套的方式,每个内部大括号表示一行元素。运行结果为[[10 20 30 40] [50 60 70 80] [90 100 110 120]]
。
同样,如果不想显式指定第一维的长度,可以使用...
来让Go语言自动推断。例如:
package main
import "fmt"
func main() {
matrix := [...][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
fmt.Println(matrix)
}
Go语言会根据内部大括号的数量自动确定第一维的长度为3。
访问二维数组元素
二维数组元素通过两个索引来访问,第一个索引表示行,第二个索引表示列,索引都从0开始。例如,对于上述定义的matrix
二维数组,访问第2行第3列(索引为(1, 2)
)的元素代码如下:
package main
import "fmt"
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
fmt.Println(matrix[1][2])
}
运行结果为70
。
二维数组元素也可以通过索引进行赋值操作。例如:
package main
import "fmt"
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
matrix[2][1] = 105
fmt.Println(matrix)
}
运行结果为[[10 20 30 40] [50 60 70 80] [90 105 110 120]]
,可以看到(2, 1)
位置的元素被成功修改为105。
遍历二维数组
- 使用嵌套for循环遍历
遍历二维数组通常使用嵌套的
for
循环。外层for
循环控制行,内层for
循环控制列。例如:
package main
import "fmt"
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
for i := 0; i < len(matrix); i++ {
for j := 0; j < len(matrix[i]); j++ {
fmt.Printf("matrix[%d][%d] = %d ", i, j, matrix[i][j])
}
fmt.Println()
}
}
上述代码通过嵌套的for
循环遍历二维数组matrix
,并输出每个元素的值。
- 使用for - range遍历
也可以使用
for - range
来遍历二维数组。例如:
package main
import "fmt"
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
for i, row := range matrix {
for j, value := range row {
fmt.Printf("matrix[%d][%d] = %d ", i, j, value)
}
fmt.Println()
}
}
这里外层for - range
返回行索引i
和行数据row
,内层for - range
对row
进行遍历,返回列索引j
和元素值value
。
多维数组作为函数参数
与一维数组类似,多维数组作为函数参数传递时,传递的也是整个数组的副本。例如,对于二维数组:
package main
import "fmt"
func modifyMatrix(mat [3][4]int) {
mat[0][0] = 100
fmt.Println("Inside function:", mat)
}
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
modifyMatrix(matrix)
fmt.Println("Outside function:", matrix)
}
运行结果为:
Inside function: [[100 20 30 40] [50 60 70 80] [90 100 110 120]]
Outside function: [[10 20 30 40] [50 60 70 80] [90 100 110 120]]
可以看到,在modifyMatrix
函数内部修改了二维数组的(0, 0)
位置的元素,但函数外部的二维数组并没有受到影响。
如果希望在函数内部修改二维数组并影响到函数外部,可以传递二维数组的指针。例如:
package main
import "fmt"
func modifyMatrixPtr(mat *[3][4]int) {
(*mat)[0][0] = 100
fmt.Println("Inside function:", *mat)
}
func main() {
matrix := [3][4]int{
{10, 20, 30, 40},
{50, 60, 70, 80},
{90, 100, 110, 120},
}
modifyMatrixPtr(&matrix)
fmt.Println("Outside function:", matrix)
}
运行结果为:
Inside function: [[100 20 30 40] [50 60 70 80] [90 100 110 120]]
Outside function: [[100 20 30 40] [50 60 70 80] [90 100 110 120]]
通过传递二维数组指针&matrix
,在modifyMatrixPtr
函数内部通过解引用指针(*mat)
来修改数组元素,从而影响到了函数外部的二维数组。
三维及更高维数组
在Go语言中,同样可以定义三维及更高维数组。三维数组的定义语法如下:
var 数组名 [第一维长度][第二维长度][第三维长度]数据类型
例如,定义一个三维整数数组:
var cube [2][3][4]int
这表示一个三维数组cube
,第一维长度为2,第二维长度为3,第三维长度为4。
初始化三维数组时,也是使用大括号嵌套的方式。例如:
package main
import "fmt"
func main() {
cube := [2][3][4]int{
{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
},
{
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24},
},
}
fmt.Println(cube)
}
访问三维数组元素需要三个索引,例如cube[1][2][3]
表示访问第二组(索引为1),第三行(索引为2),第四列(索引为3)的元素。
遍历三维数组则需要三层嵌套的for
循环或者for - range
。例如,使用嵌套for
循环遍历:
package main
import "fmt"
func main() {
cube := [2][3][4]int{
{
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
},
{
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24},
},
}
for i := 0; i < len(cube); i++ {
for j := 0; j < len(cube[i]); j++ {
for k := 0; k < len(cube[i][j]); k++ {
fmt.Printf("cube[%d][%d][%d] = %d ", i, j, k, cube[i][j][k])
}
fmt.Println()
}
fmt.Println()
}
}
随着维度的增加,代码的复杂度也会相应增加。在实际应用中,三维及更高维数组通常用于处理一些复杂的空间数据或者多层嵌套的数据结构。
多维数组与切片的关系
虽然Go语言支持多维数组,但在实际编程中,切片(Slice)的使用更为广泛。切片是动态数组,其长度可以在运行时动态变化。对于多维数据结构,使用切片的切片(Slice of Slice)来模拟多维数组往往更加灵活。
例如,使用切片的切片来模拟二维数组:
package main
import "fmt"
func main() {
matrix := make([][]int, 3)
for i := range matrix {
matrix[i] = make([]int, 4)
}
matrix[0][0] = 10
matrix[1][2] = 30
fmt.Println(matrix)
}
这里首先使用make
函数创建了一个长度为3的切片matrix
,其元素类型为[]int
,然后再为每个内部切片分配长度为4的空间。这种方式与二维数组不同,每个内部切片的长度可以根据需要动态调整。
在性能方面,由于切片是动态的,其在内存分配和管理上相对数组更为灵活,但也会带来一些额外的开销。对于固定大小且性能要求较高的多维数据存储,数组可能是更好的选择;而对于需要动态调整大小的多维数据结构,切片的切片则更为合适。
在函数参数传递方面,切片作为参数传递时,传递的是切片的描述符(包含指向底层数组的指针、长度和容量),而不是整个底层数组的副本,这使得在函数内部修改切片内容会影响到函数外部。例如:
package main
import "fmt"
func modifySliceMatrix(mat [][]int) {
mat[0][0] = 100
fmt.Println("Inside function:", mat)
}
func main() {
matrix := make([][]int, 3)
for i := range matrix {
matrix[i] = make([]int, 4)
}
matrix[0][0] = 10
modifySliceMatrix(matrix)
fmt.Println("Outside function:", matrix)
}
运行结果为:
Inside function: [[100 0 0 0] [0 0 0 0] [0 0 0 0]]
Outside function: [[100 0 0 0] [0 0 0 0] [0 0 0 0]]
可以看到,在modifySliceMatrix
函数内部修改切片的(0, 0)
位置的元素,函数外部的切片也受到了影响。
总结
在Go语言中,数组是一种基础的数据结构,用于存储固定长度的同类型元素。数组的定义、初始化、访问和遍历都有明确的规则。多维数组作为数组的扩展,用于处理更为复杂的数据结构。在实际编程中,需要根据具体需求选择合适的数据结构,数组适用于固定大小且性能要求较高的场景,而切片则更适合动态变化的情况。同时,了解数组和切片在函数参数传递等方面的特性,有助于编写高效、正确的代码。通过深入理解Go语言数组和多维数组的使用,开发者能够更好地利用这门语言进行数据处理和算法实现。