Go 入门 05数组、切片与 Map数组array、切片slice、map 是 Go 最常用的内置容器类型。新手最容易踩坑的就是 slice本篇会重点剖析其底层结构。一、数组 array数组是定长、同类型元素的集合长度是类型的一部分vara[3]int// [0 0 0]b:[3]int{1,2,3}c:[...]int{1,2,3}// 让编译器推导长度[3]int和[4]int是不同的类型不能互相赋值。数组是值类型赋值与传参都会发生完整拷贝a:[3]int{1,2,3}b:a// 拷贝b[0]99// a[0] 仍是 1实际开发中数组用得不多更多用切片。二、切片 slice2.1 切片是什么切片是对底层数组的引用视图由三部分组成slice { ptr, len, cap }ptr指向底层数组的某个元素len当前切片可见的元素个数cap从 ptr 到底层数组末尾的元素个数2.2 创建切片// 字面量s:[]int{1,2,3}// 基于数组切出arr:[5]int{1,2,3,4,5}sarr[1:4]// [2 3 4]len3, cap4// make 显式分配smake([]int,3)// len3, cap3smake([]int,3,10)// len3, cap102.3 append 与扩容s:[]int{1,2,3}sappend(s,4,5)当len cap时再次 append 会触发扩容原 cap 1024新 cap 2 × 原 cap原 cap 1024新 cap 1.25 × 原 capGo 1.18 后扩容因子有调整但思路一致几何级增长避免每次 append 都分配。2.4 切片陷阱陷阱 1共享底层数组a:[]int{1,2,3,4,5}b:a[1:3]// [2 3]b[0]99fmt.Println(a)// [1 99 3 4 5] — a 被改了陷阱 2append 后是否共享取决于是否扩容a:[]int{1,2,3,4,5}b:a[1:3]// len2, cap4bappend(b,100)// 未扩容会修改 a[3]fmt.Println(a)// [1 2 3 100 5]最佳实践明确想要副本时用copyb:make([]int,len(a))copy(b,a)陷阱 3循环里 range 变量复用// 错误示例Go 1.22 之前funcs:[]func(){}for_,v:range[]int{1,2,3}{funcsappend(funcs,func(){fmt.Println(v)})}// 输出三次 3Go 1.22 已修复该行为每次循环 v 都是独立变量。2.5 删除元素// 删除索引 isappend(s[:i],s[i1:]...)三、map3.1 创建与使用// 字面量m:map[string]int{a:1,b:2,}// makemmake(map[string]int)m[c]3// 读取不存在返回零值v:m[x]// 0v,ok:m[x]// 0, false — 用 ok 判断是否存在// 删除delete(m,a)// 长度fmt.Println(len(m))3.2 遍历fork,v:rangem{fmt.Println(k,v)}⚠️ map 的遍历顺序是随机的这是 Go 故意设计的避免开发者依赖某个顺序。3.3 map 不是并发安全的并发读写 map 会触发 panicfatal error: concurrent map writes并发场景下使用sync.RWMutexmapsync.Map适合读多写少且 key 集合相对稳定的场景varmu sync.RWMutex m:make(map[string]int)mu.Lock()m[a]1mu.Unlock()mu.RLock()v:m[a]mu.RUnlock()3.4 map 的 key 限制key 必须是可比较类型基本类型、指针、channel、interface动态类型可比较、struct所有字段可比较、array。不能作为 keyslice、map、function。四、字符串与 []byte / []runes:Hello, 世界b:[]byte(s)// 字节切片每个中文 3 字节r:[]rune(s)// rune 切片每个中文一个 runefmt.Println(len(b))// 13fmt.Println(len(r))// 9fmt.Println(string(b))// 还原回字符串五、实战单词词频统计packagemainimport(fmtstrings)funcmain(){text:go is great go is fast go is funcounts:make(map[string]int)for_,w:rangestrings.Fields(text){counts[w]}forw,n:rangecounts{fmt.Printf(%s - %d\n,w,n)}}六、小结类型长度是否引用并发安全array固定值类型-slice可变引用底层数组否map可变引用否slice {ptr, len, cap}append 可能扩容也可能不扩容想拷贝就用copymap 遍历无序并发要加锁字符串处理中文用[]rune。下一篇我们将进入 Go 的OOP世界结构体与方法。
Go 入门 05:数组、切片与 Map
发布时间:2026/5/19 10:38:42
Go 入门 05数组、切片与 Map数组array、切片slice、map 是 Go 最常用的内置容器类型。新手最容易踩坑的就是 slice本篇会重点剖析其底层结构。一、数组 array数组是定长、同类型元素的集合长度是类型的一部分vara[3]int// [0 0 0]b:[3]int{1,2,3}c:[...]int{1,2,3}// 让编译器推导长度[3]int和[4]int是不同的类型不能互相赋值。数组是值类型赋值与传参都会发生完整拷贝a:[3]int{1,2,3}b:a// 拷贝b[0]99// a[0] 仍是 1实际开发中数组用得不多更多用切片。二、切片 slice2.1 切片是什么切片是对底层数组的引用视图由三部分组成slice { ptr, len, cap }ptr指向底层数组的某个元素len当前切片可见的元素个数cap从 ptr 到底层数组末尾的元素个数2.2 创建切片// 字面量s:[]int{1,2,3}// 基于数组切出arr:[5]int{1,2,3,4,5}sarr[1:4]// [2 3 4]len3, cap4// make 显式分配smake([]int,3)// len3, cap3smake([]int,3,10)// len3, cap102.3 append 与扩容s:[]int{1,2,3}sappend(s,4,5)当len cap时再次 append 会触发扩容原 cap 1024新 cap 2 × 原 cap原 cap 1024新 cap 1.25 × 原 capGo 1.18 后扩容因子有调整但思路一致几何级增长避免每次 append 都分配。2.4 切片陷阱陷阱 1共享底层数组a:[]int{1,2,3,4,5}b:a[1:3]// [2 3]b[0]99fmt.Println(a)// [1 99 3 4 5] — a 被改了陷阱 2append 后是否共享取决于是否扩容a:[]int{1,2,3,4,5}b:a[1:3]// len2, cap4bappend(b,100)// 未扩容会修改 a[3]fmt.Println(a)// [1 2 3 100 5]最佳实践明确想要副本时用copyb:make([]int,len(a))copy(b,a)陷阱 3循环里 range 变量复用// 错误示例Go 1.22 之前funcs:[]func(){}for_,v:range[]int{1,2,3}{funcsappend(funcs,func(){fmt.Println(v)})}// 输出三次 3Go 1.22 已修复该行为每次循环 v 都是独立变量。2.5 删除元素// 删除索引 isappend(s[:i],s[i1:]...)三、map3.1 创建与使用// 字面量m:map[string]int{a:1,b:2,}// makemmake(map[string]int)m[c]3// 读取不存在返回零值v:m[x]// 0v,ok:m[x]// 0, false — 用 ok 判断是否存在// 删除delete(m,a)// 长度fmt.Println(len(m))3.2 遍历fork,v:rangem{fmt.Println(k,v)}⚠️ map 的遍历顺序是随机的这是 Go 故意设计的避免开发者依赖某个顺序。3.3 map 不是并发安全的并发读写 map 会触发 panicfatal error: concurrent map writes并发场景下使用sync.RWMutexmapsync.Map适合读多写少且 key 集合相对稳定的场景varmu sync.RWMutex m:make(map[string]int)mu.Lock()m[a]1mu.Unlock()mu.RLock()v:m[a]mu.RUnlock()3.4 map 的 key 限制key 必须是可比较类型基本类型、指针、channel、interface动态类型可比较、struct所有字段可比较、array。不能作为 keyslice、map、function。四、字符串与 []byte / []runes:Hello, 世界b:[]byte(s)// 字节切片每个中文 3 字节r:[]rune(s)// rune 切片每个中文一个 runefmt.Println(len(b))// 13fmt.Println(len(r))// 9fmt.Println(string(b))// 还原回字符串五、实战单词词频统计packagemainimport(fmtstrings)funcmain(){text:go is great go is fast go is funcounts:make(map[string]int)for_,w:rangestrings.Fields(text){counts[w]}forw,n:rangecounts{fmt.Printf(%s - %d\n,w,n)}}六、小结类型长度是否引用并发安全array固定值类型-slice可变引用底层数组否map可变引用否slice {ptr, len, cap}append 可能扩容也可能不扩容想拷贝就用copymap 遍历无序并发要加锁字符串处理中文用[]rune。下一篇我们将进入 Go 的OOP世界结构体与方法。