Go 语言数组
目录
数组是 Go 语言中最基础的线性数据结构,用于存储固定长度的同类型元素序列。在 Go 中,数组是值类型,长度是类型的一部分。理解数组是掌握切片、字符串等高级类型的前提。
数组的定义与声明
Go 中数组的类型表示为 [N]T,其中 N 是数组长度(必须是常量),T 是元素类型。
package main
import "fmt"
func main() {
// 声明一个长度为 5 的 int 数组,元素默认为零值 0
var arr [5]int
fmt.Println(arr) // [0 0 0 0 0]
fmt.Printf("类型: %T, 长度: %d\n", arr, len(arr))
}数组一旦声明,长度不可改变。[5]int 和 [10]int 是不同类型,不能互相赋值。
数组初始化
字面量初始化
package main
import "fmt"
func main() {
// 完整初始化
arr1 := [3]int{1, 2, 3}
fmt.Println(arr1) // [1 2 3]
// 编译器自动推断长度
arr2 := [...]int{10, 20, 30, 40}
fmt.Println(arr2) // [10 20 30 40]
fmt.Println(len(arr2)) // 4
}... 语法让编译器根据初始化元素个数自动推断数组长度。
指定索引初始化
package main
import "fmt"
func main() {
// 只初始化索引 1 和 4,其余元素为零值
arr := [5]string{"a": "hello", 4: "world"}
fmt.Println(arr) // [ hello world]
// 指定索引 + 自动推断长度
arr2 := [...]int{0: 100, 3: 200}
fmt.Println(arr2) // [100 0 0 200]
fmt.Println(len(arr2)) // 4
}数组遍历
使用 for 循环
package main
import "fmt"
func main() {
arr := [4]string{"Go", "Java", "Python", "Rust"}
for i := 0; i < len(arr); i++ {
fmt.Printf("arr[%d] = %s\n", i, arr[i])
}
}使用 for-range 循环
package main
import "fmt"
func main() {
arr := [4]string{"Go", "Java", "Python", "Rust"}
// 同时获取索引和值
for i, v := range arr {
fmt.Printf("索引 %d: %s\n", i, v)
}
// 只需要值,忽略索引
for _, v := range arr {
fmt.Println(v)
}
// 只需要索引
for i := range arr {
fmt.Printf("索引: %d\n", i)
}
}for-range 是 Go 中最常用的遍历方式,代码更简洁,也适用于切片、map、channel 等类型。
多维数组
Go 支持多维数组,最常见的是二维数组。
package main
import "fmt"
func main() {
// 声明并初始化一个 2x3 的二维数组
matrix := [2][3]int{
{1, 2, 3},
{4, 5, 6},
}
fmt.Println(matrix) // [[1 2 3] [4 5 6]]
fmt.Println(matrix[0]) // [1 2 3]
fmt.Println(matrix[1][2]) // 6
// 遍历二维数组
for i := range matrix {
for j := range matrix[i] {
fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j])
}
}
}多维数组在内存中仍然是连续存储的,[2][3]int 等价于 6 个 int 连续排列。
数组的值类型特性
Go 中数组是值类型。赋值或传参时,会完整拷贝整个数组。
package main
import "fmt"
func main() {
a := [3]int{1, 2, 3}
b := a // 拷贝整个数组
b[0] = 100
fmt.Println(a) // [1 2 3] —— a 不受影响
fmt.Println(b) // [100 2 3]
}这意味着:
- 大数组的赋值和传参开销较大
- 如果希望共享数据,需要传递数组指针
*[N]T - 这也是 Go 引入切片(slice)的重要原因之一
package main
import "fmt"
func modifyByPointer(arr *[3]int) {
(*arr)[0] = 999
}
func main() {
a := [3]int{1, 2, 3}
modifyByPointer(&a)
fmt.Println(a) // [999 2 3]
}数组的长度
len() 函数返回数组的长度,它在编译期就能确定,因为长度是数组类型的一部分。
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
fmt.Println(len(arr)) // 5
// len 返回的是 int 类型
fmt.Printf("len 的类型: %T\n", len(arr)) // int
}对于多维数组,len() 返回最外层的长度:
package main
import "fmt"
func main() {
matrix := [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Println(len(matrix)) // 2
fmt.Println(len(matrix[0])) // 3
}数组的局限性
数组虽然简单高效,但存在明显局限:
- 长度固定:声明后无法增减元素
- 值类型拷贝:大数组传参开销大
- 无法比较:Go 中数组不支持
==比较(除非元素类型本身可比较,但实际使用中很少直接比较数组) - 缺少实用方法:没有内置的排序、查找等操作
package main
import "fmt"
func main() {
var arr [3]int
// arr = append(arr, 1) // 编译错误!append 只能用于切片
// 无法动态扩容
// arr[3] = 100 // panic: index out of range
fmt.Println(arr)
}正是因为这些局限,Go 提供了切片(slice) 作为日常开发中的首选数据结构。切片在数组之上提供了动态长度、引用语义等能力,下一节我们将详细讲解。
总结
本篇介绍了 Go 语言数组的核心知识:数组的定义与声明([N]T)、多种初始化方式、for 和 for-range 遍历、多维数组、值类型特性以及 len() 函数。数组作为固定长度的值类型,在内存中连续存储,访问效率高但灵活性不足。在实际开发中,我们更常使用切片来替代数组,因为切片提供了动态长度和引用语义,更加灵活方便。