Go-02-数组、切片与 Map
本节目标:掌握 Go 的数组、切片(slice)、Map(哈希表)三大集合类型,理解切片的扩容机制,并初步接触泛型。
1. 数组(Array)
数组是固定长度的、值类型(赋值时复制整个底层数据):
1 | var a [5]int // 零值数组: [0 0 0 0 0] |
数组的关键特性:
- 长度是类型的一部分:
[3]int和[5]int是不同类型 - 是值类型,传参时会复制整个数组(大数组开销大)
- 大小固定,不能 append
1 | a := [3]int{1, 2, 3} |
遍历:
1 | for i, v := range a { |
多维数组:
1 | var matrix [3][4]int |
2. 切片(Slice)—— Go 的核心数据结构
切片是对数组的引用,是变长的。底层结构:
1 | type slice struct { |
2.1 创建切片
1 | // 从数组切 |
2.2 切片操作
1 | s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} |
2.3 append:扩容
1 | s := []int{1, 2, 3} |
2.4 copy:复制切片
1 | src := []int{1, 2, 3, 4, 5} |
2.5 切片陷阱
共享底层数组导致意外修改:
1 | arr := [5]int{1, 2, 3, 4, 5} |
append 后切片还指向旧数组:
1 | s1 := []int{1, 2, 3} |
2.6 删除元素
Go 没有内置 delete-by-index,要自己实现:
1 | s := []int{1, 2, 3, 4, 5} |
保留顺序 vs 不保留顺序:
1 | // 保留顺序(O(n),慢) |
3. Map(哈希表)
3.1 创建
1 | // 字面量 |
3.2 操作
1 | m := map[string]int{"a": 1, "b": 2} |
3.3 Map 陷阱
- Map 不是并发安全的!多 goroutine 同时读写会 panic。用
sync.Map或加锁。 - 遍历顺序不保证(每次可能不同)。
- Key 必须是可比较类型:
bool, 数字, 字符串, 指针, 数组, 接口, 结构体(如果字段都可比较)。slice, map, func 不能作 key。
4. 字符串与字节切片
字符串是不可变的字节序列。可以用 []byte / []rune 转换:
1 | s := "Hello, 世界" |
5. 泛型入门(Go 1.18+)
Go 在 1.18 引入了泛型,类型参数用 [T any] 或 [T constraints.X] 声明:
5.1 基本泛型函数
1 | // 任意类型的"打切片"函数 |
5.2 泛型类型
1 | // 通用 Set |
5.3 类型约束
1 | import "golang.org/x/exp/constraints" |
类型约束的本质:泛型让”参数多态”在编译期由编译器检查,不影响运行时性能(Go 泛型是单态化 monomorphization,不像 Java 那样擦除)。
6. 实战:手写一个 int 集合
1 | package main |
小结
| 集合 | 长度可变 | 值/引用 | 适用场景 |
|---|---|---|---|
数组 [N]T |
❌ | 值 | 固定大小(少见) |
切片 []T |
✅ | 引用(共享底层) | 90% 的场景 |
Map map[K]V |
✅ | 引用 | 键值查找、计数 |
关键心智模型:
- 切片是小胖手指:指向底层数组的
{ptr, len, cap}三元组 - 多个切片共享底层数组 → 修改要小心
- append 容量不足时扩容复制
- Map 的零值是 nil,可以读不能写
下一节讲字符串处理和字符编码(UTF-8 / rune / byte 的三角关系)。
- 本文作者: CoderSong
- 本文链接: https://jack-song-gif.github.io/2026/08/31/Go-02-数组、切片与Map/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!