Slice vs Array´
在 Go 语言中,有两种常见的数据结构可用于表示「序列数据」:Array 与 Slice。 这两者在语法上相似,但背后行为与使用情境差异非常关键。简单来说,Array 是固定长度(Static Array),而 Slice 是动态长度(Dynamic Array)。
Array
// 声明 5 个 int 的 Arrayarray := [5]int{1, 2, 3, 4, 5}
// 访问或修改元素array[0] = 100
// 让 compiler 自动推断数组长度b := [...]string{"Penn", "Teller"}Slice
Slice 建立于 Array 之上,它并不直接存放数据,而是指向底层 Array,同时记录当前的长度与容量。
func main() { // 创建 Slice(长度 3,容量 5) s := make([]int, 3, 5) fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5
// 修改与读取 s[0] = 10 s[1] = 20 s[2] = 30
// 扩充 s = append(s, 40, 50) fmt.Println(s, len(s), cap(s)) // [10 20 30 40 50] 5 5
// 扩充 s = append(s, 60) fmt.Println(s, len(s), cap(s)) // [10 20 30 40 50 60] 6 10
// 子 Slice 切片 sub := s[1:4] fmt.Println(sub) // [20 30 40]}了解 Slice 的自动扩充机制
当对 Slice 执行 append 时:
- 若底层 Array 还有空间 → 直接延伸长度(O(1))
- 若容量不足 → Go 会建立一个更大的 Array,将旧数据复制过去(O(n))
func main() { // 均摊分析 Amortized analysis s := []int{1} fmt.Println(len(s), cap(s)) // 1 1, O(1)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 2 2, O(1)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 3 4, O(n)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 4 4, O(1)
s = append(s, 1) fmt.Println(len(s), cap(s)) // 5 8, O(n)}可以看到容量大多倍增成长,这样的自动扩容策略兼顾了性能与内存利用率。
Range
遍历数组内容。
func main() { s := []string{"apple", "banana", "cherry"}
for i, v := range s { fmt.Println(i, v) }}
// 0 apple// 1 banana// 2 cherry: 切片
: 用来提取 Slice 部分区间,格式为 slice[low:high] 代表从索引 low 开始(包含),直到索引 high 结束(不包含):
func main() { s := []int{10, 20, 30, 40, 50}
fmt.Println(s[1:3]) // [20 30] fmt.Println(s[:2]) // [10 20],省略前界代表 0 fmt.Println(s[2:]) // [30 40 50],省略后界代表 len(s)}s[a:b] 虽然是创建一个新的 Slice,但仍共享相同的底层 array,修改 sub Slice 的内容,会影响原始数据:
sub := s[1:3]sub[0] = 99fmt.Println(s) // [10 99 30 40 50]fmt.Println(sub) // [99 30]Copy
由于 Slice 共享底层 Array,若要建立完全独立的副本,应使用 copy():
s := []int{1, 2, 3}t := make([]int, len(s))copy(t, s)
t[0] = 99fmt.Println(s) // [1 2 3]fmt.Println(t) // [99 2 3]总结
| 比较项 | Array | Slice |
|---|---|---|
| 长度 | 固定 | 可变 |
| 类型定义 | [N]T | []T |
| 是否含容量信息 | 否 | 是 |
| 是否可扩充 | 否 | 是(append) |
| 传递开销 | 复制整个数组 | 传引用(轻量) |
| 适用场景 | 已知固定长度 | 不确定长度、需灵活增减数据 |
可到 Codewars 找 Go Array 相关题目 开始刷题。
延伸阅读
- Go Slices: usage and internals - go.dev
- [Golang] Array and Slice - PJCHENder
- If you are a beginner in Go, avoid this Slice pitfall! - Flo Woelki
- Go Class: 10 Slices in Detail - Matt KØDVB