第5章:数组、切片与 Map
5.1 数组
数组是固定长度的同类型元素序列。
声明与初始化
// 声明数组vararr1[5]int// [0 0 0 0 0]// 初始化arr2:=[5]int{1,2,3,4,5}// 自动计算长度arr3:=[...]int{1,2,3,4,5}// 指定索引初始化arr4:=[5]int{1:10,3:30}// [0 10 0 30 0]// 多维数组matrix:=[2][3]int{{1,2,3},{4,5,6},}数组操作
arr:=[5]int{1,2,3,4,5}// 访问元素fmt.Println(arr[0])// 1// 修改元素arr[0]=10// 遍历fori,v:=rangearr{fmt.Printf("索引: %d, 值: %d\n",i,v)}// 数组长度fmt.Println(len(arr))// 5数组特性
// 数组是值类型(赋值会复制)a:=[3]int{1,2,3}b:=a b[0]=10fmt.Println(a)// [1 2 3] - a 不受影响fmt.Println(b)// [10 2 3]// 比较(相同类型数组可以比较)a1:=[3]int{1,2,3}a2:=[3]int{1,2,3}fmt.Println(a1==a2)// true5.2 切片
切片是动态长度的序列,是对底层数组的抽象。
创建切片
// 使用字面量s1:=[]int{1,2,3,4,5}// 使用 make 创建s2:=make([]int,5)// 长度为5,容量为5s3:=make([]int,0,10)// 长度为0,容量为10// 从数组/切片创建arr:=[5]int{1,2,3,4,5}s4:=arr[1:3]// [2 3]s5:=arr[:3]// [1 2 3]s6:=arr[2:]// [3 4 5]s7:=arr[:]// [1 2 3 4 5]切片操作
s:=[]int{1,2,3,4,5}// 添加元素s=append(s,6)// [1 2 3 4 5 6]s=append(s,7,8,9)// [1 2 3 4 5 6 7 8 9]// 复制切片dst:=make([]int,len(s))copy(dst,s)// 删除元素(索引2)s=append(s[:2],s[3:]...)// 插入元素(在索引2处插入10)s=append(s[:2+1],s[2:]...)s[2]=10// 切片表达式s=[]int{1,2,3,4,5}s2:=s[1:3]// [2 3]s3:=s[:3]// [1 2 3]s4:=s[2:]// [3 4 5]切片长度与容量
s:=make([]int,5,10)fmt.Println("长度:",len(s))// 5fmt.Println("容量:",cap(s))// 10// 追加元素时,容量不足会触发扩容s=append(s,1,2,3)fmt.Println("长度:",len(s))// 8fmt.Println("容量:",cap(s))// 10// 继续追加,容量翻倍s=append(s,4,5,6)fmt.Println("长度:",len(s))// 11fmt.Println("容量:",cap(s))// 20切片是引用类型
// 切片共享底层数组s1:=[]int{1,2,3,4,5}s2:=s1[1:3]s2[0]=20fmt.Println(s1)// [1 20 3 4 5] - s1 也受影响// 使用 copy 避免共享s3:=make([]int,len(s2))copy(s3,s2)s3[0]=200fmt.Println(s2)// [20 3] - s2 不受影响5.3 Map
Map 是键值对的无序集合。
创建 Map
// 使用字面量m1:=map[string]int{"张三":25,"李四":30,}// 使用 make 创建m2:=make(map[string]int)// 添加元素m2["王五"]=35// 删除元素delete(m2,"王五")// 检查键是否存在age,ok:=m2["张三"]ifok{fmt.Println("年龄:",age)}// 获取长度fmt.Println(len(m2))Map 遍历
m:=map[string]int{"张三":25,"李四":30,"王五":35,}// 遍历(顺序随机)forname,age:=rangem{fmt.Printf("%s: %d岁\n",name,age)}// 只遍历键forname:=rangem{fmt.Println(name)}// 只遍历值for_,age:=rangem{fmt.Println(age)}Map 作为集合
// 使用空结构体作为值(不占内存)set:=map[string]struct{}{"apple":{},"banana":{},"cherry":{},}// 添加元素set["date"]=struct{}{}// 检查元素是否存在if_,exists:=set["apple"];exists{fmt.Println("apple 存在")}// 删除元素delete(set,"banana")Map 作为计数器
// 统计字符出现次数funccountChars(sstring)map[rune]int{count:=make(map[rune]int)for_,ch:=ranges{count[ch]++}returncount}result:=countChars("hello world")forch,count:=rangeresult{fmt.Printf("%c: %d\n",ch,count)}5.4 切片与 Map 的最佳实践
预分配内存
// 不推荐:频繁扩容vars[]intfori:=0;i<1000;i++{s=append(s,i)}// 推荐:预分配容量s:=make([]int,0,1000)fori:=0;i<1000;i++{s=append(s,i)}// Map 同理m:=make(map[string]int,1000)安全地访问 Map
// 使用 ok 模式检查键是否存在funcgetAge(mmap[string]int,namestring)(int,bool){age,ok:=m[name]returnage,ok}// 使用 sync.Map 并发安全(见并发章节)5.5 练习
- 实现一个函数,合并两个切片并去重
- 实现一个简单的 LRU 缓存(使用切片和 map)
- 编写程序统计一段文本中每个单词的出现次数
- 实现一个函数,将 map 的键和值互换
5.6 下一章
下一章我们将学习 Go 语言的结构体与接口。