跳到主要内容

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
  • 忘记数组按值复制,误以为函数修改参数会影响调用方。
  • 直接比较包含不可比较元素的数组,或试图把切片作为数组键。
  • 从数组创建切片后忽略共享关系,导致修改相互影响。

小结

  • 数组长度属于类型,数组具有完整零值。
  • 数组赋值和参数传递会复制所有元素。
  • 元素可比较时数组可比较、可作为映射键。
  • 数组切片共享数组存储;动态序列通常应优先使用切片。