Array(数组)
数组 [N]T 保存固定数量、相同类型的元素。长度是数组类型的一部分,因此 [2]int 和 [3]int 是不同类型。需要动态长度时通常使用切片,但数组是理解复制、值传递和底层存储的基础。
创建与初始化
数组字面量可以按位置或按索引初始化;未指定的元素取得零值:
package main
import "fmt"
func main() {
positions := [3]string{"first", "second", "third"}
flags := [4]bool{0: true, 2: true}
levels := [...]string{"debug", "info", "error"}
fmt.Println(positions)
fmt.Println(flags, len(flags))
fmt.Println(levels, len(levels))
}
[...]T 让编译器根据最大索引或元素数量推断长度。数组变量声明后立即具有完整的零值,不需要先调用构造函数。
长度属于类型
不同长度的数组不能直接赋值,也不能直接传给要求另一种数组类型的函数:
package main
import "fmt"
func printPair(values [2]int) {
fmt.Println(values)
}
func main() {
pair := [2]int{1, 2}
printPair(pair)
// values := [3]int{1, 2, 3}
// printPair(values) // 编译错误:[3]int 不是 [2]int
}
需要处理任意长度的序列时,函数通常接收 []T,或在确实需要固定长度契约时使用泛型约束(见 Generic(泛型))。
复制与按值传递
数组赋值、函数参数传递和返回值都会复制数组的所有元素:
package main
import "fmt"
func update(values [2]int) {
values[0] = 99
}
func main() {
original := [2]int{1, 2}
cloned := original
cloned[0] = 9
update(original)
fmt.Println(original) // [1 2]
fmt.Println(cloned) // [9 2]
}
如果元素本身是切片、映射、指针或接口,复制的是元素中保存的值,内部引用的数据仍可能共享。深复制需要按领域数据结构显式实现。
遍历、比较与切片
len 返回数组长度,range 返回索引和元素副本。数组在元素类型可比较时也可使用 == 和 !=,并可以作为映射键:
package main
import "fmt"
func main() {
left := [2]int{1, 2}
right := [2]int{1, 2}
lookup := map[[2]int]string{left: "point"}
for index, value := range left {
fmt.Println(index, value)
}
fmt.Println(left == right, lookup[right])
}
从数组或数组指针创建切片后,切片会共享数组存储;修改切片元素会修改数组。数组不能与切片直接比较。
与 TypeScript 数组的关键差异
TypeScript 的 Array<T> 长度不是类型的一部分,Go 的数组长度却参与类型检查并决定复制成本。Go 的切片才更接近 TypeScript 常用的动态数组,而且切片还具有容量和底层数组共享语义。
常见错误
- 把
[N]T当成长度可变的容器;动态长度应使用[]T。 - 忘记数组按值复制,误以为函数修改参数会影响调用方。
- 直接比较包含不可比较元素的数组,或试图把切片作为数组键。
- 从数组创建切片后忽略共享关系,导致修改相互影响。
小结
- 数组长度属于类型,数组具有完整零值。
- 数组赋值和参数传递会复制所有元素。
- 元素可比较时数组可比较、可作为映射键。
- 数组切片共享数组存储;动态序列通常应优先使用切片。