数组、切片与 Map
Go 中数组、切片、Map 是最常用的复合类型。日常开发中几乎只用切片,很少直接用数组。
数组
长度是类型的一部分,[3]int 与 [5]int 是不同类型:
1 2 3 4 5 6 7 8
| var a [3]int b := [3]int{1, 2, 3} c := [...]int{1, 2, 3}
d := b d[0] = 100
|
切片(Slice)
切片是对数组的动态视图,包含指针、长度 len、容量 cap 三个属性。
创建
1 2 3 4 5 6 7 8 9 10
| s1 := []int{1, 2, 3}
s2 := make([]int, 5) s3 := make([]int, 3, 10)
arr := [5]int{1, 2, 3, 4, 5} s4 := arr[1:4]
|
常用操作
1 2 3 4 5 6 7 8 9 10 11 12
| s := []int{1, 2, 3}
len(s) cap(s)
s = append(s, 4) s = append(s, 5, 6, 7)
sub := s[1:3] sub = s[:3] sub = s[2:]
|
append 与扩容
append 可能触发底层数组扩容,返回新切片(可能指向新数组):
1 2 3 4 5
| s1 := []int{1, 2, 3} s2 := append(s1, 4)
|
注意:循环中向切片追加元素时,不要对 range 的变量直接 append 到同一切片,容易踩坑。
拷贝
1 2 3
| src := []int{1, 2, 3} dst := make([]int, len(src)) copy(dst, src)
|
nil 切片
1 2 3 4 5
| var s []int s == nil
s = append(s, 1)
|
Map
Map 是键值对集合,引用类型,必须 make 或字面量初始化后才能写入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| m1 := map[string]int{ "apple": 5, "banana": 3, }
m2 := make(map[string]int)
m2["key"] = 42 v, ok := m2["key"] v, ok = m2["none"]
delete(m2, "key")
for k, v := range m2 { fmt.Println(k, v) }
|
零值与并发
1 2
| var m map[string]int m["a"] = 1
|
Map 不是并发安全的,并发读写需用 sync.Map 或加锁。
切片 vs 数组 vs Map 选型
| 场景 |
推荐 |
| 固定长度、值语义 |
数组(极少) |
| 动态列表、队列 |
切片 |
| 键值查找、计数 |
Map |
| 并发 Map |
sync.Map 或 map + Mutex |
小结
- 数组是值类型、定长;切片是引用类型、动态,日常首选
append 可能扩容,注意共享底层数组的副作用
- Map 需初始化后使用,用
v, ok := m[key] 判断 key 是否存在