Range循环和切片

今天我们继续讨论 切片(slice)。在本节中,我们将掌握:

  • 如何复制切片

  • 如何向切片追加元素

  • 如何使用 range 关键字遍历切片或数组


使用 range 遍历数组和切片

首先,假设我们有一个切片:

go 复制代码
var s = []int{0, 10, -3, 5, 99}

代码说明:

声明了一个 int 类型的切片 s,包含 5 个元素。


使用普通 for 循环遍历切片

go 复制代码
for index := 0; index < len(s); index++ {
    // 处理切片元素
}

代码说明:

  • 使用索引 index0 遍历到 len(s)-1

  • 通过 s[index] 访问每个元素


使用 for range 循环

go 复制代码
for index := range s {
    // 处理切片元素
}

代码说明:

  • range s 会自动遍历切片的索引

  • 逻辑上与上一个 for 循环等价

  • 代码更简洁、可读性更好


同时获取索引和值

go 复制代码
for index, element := range s {
    // 处理切片元素
}

代码说明:

  • index:当前元素的索引

  • element:当前元素的 副本(copy)


只需要元素值,不需要索引

go 复制代码
for _, element := range s {
    // 处理切片元素
}

代码说明:

  • 使用 _ 忽略索引

  • 如果声明了但未使用索引变量,Go 编译器会报错


range 中的元素是副本(只读)

go 复制代码
for _, element := range s {
    element = newValue   // 这个赋值不会生效
}                        // 原切片中的元素不会改变

代码说明:

  • element 是切片元素的拷贝

  • 修改它不会影响原切片


正确修改切片元素的方式

go 复制代码
for index := range s {
    s[index] = newValue
}

代码说明:

  • 通过索引直接访问并修改切片中的元素

  • 这是修改切片内容的正确方法


copy 函数

Go 提供了一个内置函数 copy 用来复制切片:

go 复制代码
copy(destination, source)
  • 第一个参数:目标切片

  • 第二个参数:源切片

  • 返回值:成功复制的元素个数

除了一个特殊情况(字符串),copy 只能用于切片

字符串的情况会在后续章节介绍


copy 示例

go 复制代码
var s = []int{12, 23, 34}
var sn = make([]int, len(s))

var n = copy(sn, s)  // n 为复制的元素个数

sn[0] = 0
sn[1] = 11

fmt.Println(n)  // 3
fmt.Println(s)  // [12 23 34]
fmt.Println(sn) // [0 11 34]

代码说明:

  • sns 的真正副本

  • 修改 sn 不会影响 s

  • n 等于 len(s),即 3


目标切片长度为 0 的情况

go 复制代码
var s = []int{12, 23, 34}
var sn []int

var n = copy(sn, s)

fmt.Println(n)  // 0
fmt.Println(sn) // []

代码说明:

  • sn 的长度为 0,没有可用空间

  • copy 不会复制任何元素


不关心返回值时

go 复制代码
copy(sn, s)

代码说明:

  • 如果复制的数量不重要,可以直接忽略返回值

append 函数

切片最重要的特性是 长度可变

Go 使用内置函数 append 向切片末尾追加元素。


向切片追加元素

go 复制代码
var s = []int{12, 23, 34}
s = append(s, 45)
fmt.Println(s) // [12 23 34 45]

s = append(s, 56, 67)
fmt.Println(s) // [12 23 34 45 56 67]

代码说明:

  • append 可以一次添加一个或多个元素

  • 它会返回一个新的切片,必须接收返回值


追加一个切片到另一个切片

go 复制代码
var s1 = []int{12, 23, 34}
var s2 = []int{45, 56, 67}

var s = append(s1, s2...)

代码说明:

  • ... 表示将 s2 拆分为多个参数

  • 可以把一个切片整体追加到另一个切片中


等价写法(仅用于理解):

go 复制代码
var s = append(s1, s2[0], s2[1], s2[2])

说明:

  • 实际中切片长度不固定,因此必须使用 ...

向 nil 切片追加元素

go 复制代码
var s []int
s = append(s, 10)

代码说明:

  • append 可以自动初始化一个 nil 切片

  • 最终 s[10]


切片的分配与重新分配(allocation & reallocation)

当切片容量不足时,append 会:

  1. 创建一个更大的底层数组

  2. 将原数据复制过去

为了减少这种开销,提前分配足够的容量是一个好习惯


预先知道容量的情况

go 复制代码
var a = []int{1, 2, 4, 3, 6}
var b = []int{-1, 9, -90}

var s = make([]int, 0, len(a)+len(b))
s = append(s, a...)
s = append(s, b...)

代码说明:

  • 提前设置好容量,避免多次重新分配

根据经验预估容量

go 复制代码
var countries = make([]string, 0, 2000)

代码说明:

  • 长度为 0,容量为 2000

  • 适合大量 append 操作


只指定长度的情况

go 复制代码
var countries = make([]string, 2000)
countries = append(countries, "Indonesia")

fmt.Println("cap:", cap(countries)) // cap: 3072

代码说明:

  • 初始 len = cap = 2000

  • 一旦 append,容量会自动扩展(重新分配)


直接通过索引赋值不会扩容

go 复制代码
var countries = make([]string, 2000)
for i := range countries {
    countries[i] = "Indonesia"
}

fmt.Println("cap:", cap(countries)) // cap: 2000

代码说明:

  • 没有使用 append

  • 容量保持不变


总结

  • 使用 for range 遍历数组和切片

  • range 中的元素是副本,不能直接修改

  • copy 函数用于复制切片

  • append 函数用于扩展切片

  • 切片的容量、重新分配以及性能优化技巧

相关推荐
Jolyne_2 分钟前
Taro小程序接入微信客服过程记录
前端
勇往直前plus3 分钟前
前端三基石:从后端视角理解 HTML、CSS 与 JavaScript
前端·css·html
用户693717500138415 分钟前
Google 推 AppFunctions:手机上的 AI 终于能自己干活了
android·前端·人工智能
用户693717500138432 分钟前
AI让编码变简单,真正拉开差距的是UI设计和产品思考
android·前端·人工智能
孟祥_成都36 分钟前
别再吹牛了,100% Vibe Coding 存在无法自洽的逻辑漏洞!
前端·ai编程·vibecoding
腾讯云云开发37 分钟前
用 OpenClaw + CloudBase 自动化开发网站:30分钟从安装到上线
后端·ai编程
独断万古他化1 小时前
【Java 实战项目】多用户网页版聊天室:消息传输模块 —— 基于 WebSocket 实现实时通信
java·spring boot·后端·websocket·ajax·mybatis
舒一笑1 小时前
🚀 我用一行命令,把 OSS 私有文件变成“可直接下载的公网链接”(很多人不会)
后端
m0_564914921 小时前
AI学习课堂网站丨OPENMAIC丨清华团队开源项目
学习
小兔崽子去哪了1 小时前
Docker 安装 PostgreSQL
数据库·后端·postgresql