当前位置: 首页 > news >正文

Go 入门 05:数组、切片与 Map

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

实际开发中数组用得不多,更多用切片。

二、切片 slice

2.1 切片是什么

切片是对底层数组的引用视图,由三部分组成:

slice = { ptr, len, cap }
  • ptr:指向底层数组的某个元素
  • len:当前切片可见的元素个数
  • cap:从 ptr 到底层数组末尾的元素个数

2.2 创建切片

// 字面量s:=[]int{1,2,3}// 基于数组切出arr:=[5]int{1,2,3,4,5}s=arr[1:4]// [2 3 4],len=3, cap=4// make 显式分配s=make([]int,3)// len=3, cap=3s=make([]int,3,10)// len=3, cap=10

2.3 append 与扩容

s:=[]int{1,2,3}s=append(s,4,5)

len == cap时再次 append 会触发扩容:

  • 原 cap < 1024:新 cap = 2 × 原 cap
  • 原 cap >= 1024:新 cap = 1.25 × 原 cap

Go 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 被改了!
陷阱 2:append 后是否共享取决于是否扩容
a:=[]int{1,2,3,4,5}b:=a[1:3]// len=2, cap=4b=append(b,100)// 未扩容,会修改 a[3]fmt.Println(a)// [1 2 3 100 5]

最佳实践:明确想要副本时用copy

b:=make([]int,len(a))copy(b,a)
陷阱 3:循环里 range 变量复用
// 错误示例(Go 1.22 之前)funcs:=[]func(){}for_,v:=range[]int{1,2,3}{funcs=append(funcs,func(){fmt.Println(v)})}// 输出三次 3

Go 1.22 已修复该行为,每次循环 v 都是独立变量。

2.5 删除元素

// 删除索引 is=append(s[:i],s[i+1:]...)

三、map

3.1 创建与使用

// 字面量m:=map[string]int{"a":1,"b":2,}// makem=make(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 会触发 panic:

fatal error: concurrent map writes

并发场景下使用:

  1. sync.RWMutex+map
  2. sync.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。

不能作为 key:slice、map、function。

四、字符串与 []byte / []rune

s:="Hello, 世界"b:=[]byte(s)// 字节切片,每个中文 3 字节r:=[]rune(s)// rune 切片,每个中文一个 runefmt.Println(len(b))// 13fmt.Println(len(r))// 9fmt.Println(string(b))// 还原回字符串

五、实战:单词词频统计

packagemainimport("fmt""strings")funcmain(){text:="go is great go is fast go is fun"counts:=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 可能扩容也可能不扩容;
  • 想拷贝就用copy
  • map 遍历无序,并发要加锁;
  • 字符串处理中文用[]rune

下一篇我们将进入 Go 的"OOP"世界:结构体与方法

http://www.cnnetsun.cn/news/2449735.html

相关文章:

  • LRCGET:如何为你的本地音乐库实现智能歌词同步?
  • 英创ARM9工控主板Linux应用自动启动方案与实战
  • WebPlotDigitizer完整指南:如何5分钟内从图表图像提取科研数据
  • OBS多平台同步直播插件:一键开启全网覆盖的直播新时代
  • CircuitJS1电路仿真器:3步搭建你的虚拟电子实验室
  • 别再被跨域图片坑了!html2canvas.js 0.5.0-beta4 完整配置指南(附useCORS和proxy实战)
  • 深度解析RePKG:解锁Wallpaper Engine壁纸资源的专业工具
  • 告别OnlyOffice限制!用Alist+KkFileView搭建全能文件预览服务(支持PDF/图片/压缩包等)
  • 【亲测免费】 探秘爱的数字化邀请——微信小程序婚礼邀请函开源项目推荐
  • 从Scene到Game:深度解析Unity中Align With View的工作原理与实战应用
  • 单文件产出知识参考库技能singlefile-output-reference
  • 逆向新手看过来:手把手教你用LSPosed+FunDex2,给APK‘扒衣服’看源码
  • 如何高效获取网盘直链:LinkSwift完整使用指南与配置教程
  • 【免费下载】 批量GetShell工具新版:自动化漏洞利用的利器
  • 从网站点击量到疾病发病率:泊松回归模型在业务中的5个真实应用场景与R实现
  • Pydantic序列化避坑大全:从‘按声明类型序列化’到灵活exclude/include的5个常见误区
  • LeaguePrank终极指南:3分钟掌握英雄联盟个人信息自定义
  • 【亲测免费】 探索高效PCB设计:FreeRouting插件助力KiCad自动布线
  • 从单人游戏到多人派对:Nucleus Co-Op如何重新定义本地合作游戏体验
  • 【免费下载】 北斗接收机设计MATLAB代码:BDS-3 B1C/B2a SDR接收器【matlab下载】
  • Vivado 2018.3在Win10下死活连不上JTAG?别慌,这份保姆级驱动修复指南帮你搞定ZYQN-XC7Z020
  • 【亲测免费】 拥抱高效数据处理:PHPExcel 7.4 版本适配资源推荐
  • Lumerical MODE新手避坑指南:从材料导入到S矩阵,手把手搞定EME Solver
  • 如何快速掌握CircuitJS1:免费高效的浏览器电路仿真终极指南
  • 【亲测免费】 慧荣SM2258XT固态硬盘修复神器:HUAXUAN 铧煊S800 480G开卡软件推荐
  • ADS版图封装实战:从零创建ATF54143和0603封装,并一键注入电感模型
  • 5分钟掌握ncmdumpGUI:网易云NCM文件一键解密终极指南
  • 掌握C TCP通信:高效实现服务端与客户端数据交互
  • 用C++模拟堆宝塔游戏:PTA L2-045题解与STL vector实战
  • 百度季报图解:营收321亿 AI业务占比首次过半 DAA重塑AI价值标准