目录
数组与切片 (Arrays and Slices)
在 Go 语言中,数组和切片是两个紧密相关但用途迥异的数据结构。新手常因为混淆它们而踩坑。
1. 数组 (Array)
数组是固定长度的、同一类型的数据集合。
1.1 定义与初始化
数组的长度是类型的一部分,[3]int 和 [4]int 是完全不同的两种类型。
go
var a1 [3]int // 默认初始化为 [0 0 0]
var a2 = [3]int{1, 2, 3} // 指定初始值
a3 := [...]int{1, 2, 3, 4, 5} // 自动推断长度为 51.2 值类型特性
Go 语言中的数组是值类型。这意味着将一个数组赋值给另一个数组,或者作为函数参数传递时,会发生完全拷贝(Copy)。
go
a := [3]int{1, 2, 3}
b := a // b 是 a 的完整副本
b[0] = 100
fmt.Println(a) // [1 2 3] (a 不受影响)
fmt.Println(b) // [100 2 3]2. 切片 (Slice)
切片是对数组一个连续片段的引用。它更加灵活,长度可变,是 Go 开发中最常用的数据结构。
2.1 内部结构
切片本质上是一个结构体,包含三个字段:
- Pointer: 指向底层数组中切片开始位置的指针。
- Len: 切片当前的长度(元素个数)。
- Cap: 切片的容量(从切片开始位置到底层数组末尾的长度)。
2.2 创建切片
方式一:从数组切取
go
arr := [...]int{1, 2, 3, 4, 5}
s1 := arr[1:3] // 包含 arr[1], arr[2],即 [2 3]方式二:直接声明
go
var s []int // nil 切片,len=0, cap=0方式三:使用 make (推荐)
go
// make([]Type, len, cap)
s2 := make([]int, 5, 10) // 长度为5,容量为102.3 切片的追加 (append)
使用 append 向切片追加元素。如果切片容量不够,Go 会自动扩容(分配一个新的更大的底层数组,并将旧数据拷贝过去)。
go
var s []int
s = append(s, 1) // [1]
s = append(s, 2, 3, 4) // [1 2 3 4]
s = append(s, s...) // 追加另一个切片 [1 2 3 4 1 2 3 4]扩容策略
当容量小于 256 时,每次扩容通常是翻倍;当容量较大时,扩容系数会逐渐降低(约 1.25 倍),以节省内存。
2.4 切片的拷贝 (copy)
由于切片是引用类型,简单的赋值 s2 := s1 只是拷贝了指针(浅拷贝)。如果需要独立的副本,必须使用 copy。
go
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src) // 将 src 内容拷贝到 dst
dst[0] = 100
fmt.Println(src) // [1 2 3] (不受影响)3. 性能优化建议
3.1 预分配容量
如果你知道大概需要存储多少元素,建议在 make 时指定 cap,避免频繁的内存分配和扩容。
go
// ❌ 差:多次扩容
s := make([]int, 0)
for i := 0; i < 1000; i++ {
s = append(s, i)
}
// ✅ 优:一次分配
s := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
s = append(s, i)
}3.2 避免内存泄漏
如果你有一个很大的数组,但只切片引用了其中一小部分,整个大数组都无法被垃圾回收(GC)。
go
func getSmallSlice() []int {
bigArr := [1000000]int{...}
// ❌ 导致 bigArr 无法释放
return bigArr[:10]
}
func getSmallSliceFixed() []int {
bigArr := [1000000]int{...}
small := make([]int, 10)
// ✅ 拷贝数据,bigArr 可以被回收
copy(small, bigArr[:10])
return small
}扫描下方二维码,私信【训练营】可进行咨询和报名。 
