Go循环语句range
Go range关键字用于遍历集合类型,语法简洁统一。
range基本语法
基本结构
Go
for 索引, 值 := range 集合 {
// 处理每个元素
}
// 简化形式
for 索引 := range 集合 {
// 只使用索引
}
for _, 值 := range 集合 {
// 只使用值
}
遍历slice/array
基本遍历
Go
nums := []int{10, 20, 30}
// 索引和值
for i, v := range nums {
fmt.Println(i, v)
}
// 0 10
// 1 20
// 2 30
// 只索引
for i := range nums {
fmt.Println(i)
}
// 只值
for _, v := range nums {
fmt.Println(v)
}
注意:值是副本
Go
nums := []int{1, 2, 3}
for _, v := range nums {
v *= 2 // 修改副本,不影响原slice
}
fmt.Println(nums) // [1, 2, 3]
// 需修改原slice用索引
for i := range nums {
nums[i] *= 2
}
fmt.Println(nums) // [2, 4, 6]
range返回的值是元素副本,修改不影响原集合。
遍历map
基本遍历
Go
m := map[string]int{"a": 1, "b": 2}
// 键和值
for key, value := range m {
fmt.Println(key, value)
}
// 只键
for key := range m {
fmt.Println(key)
}
// 只值
for _, value := range m {
fmt.Println(value)
}
map遍历顺序随机,每次顺序可能不同。
遍历string
按字符遍历
Go
s := "hello世界"
// 遍历字符(Unicode码点)
for i, c := range s {
fmt.Println(i, c, string(c))
}
// 0 104 h
// 1 101 e
// ...
// 5 19990 世 (中文字符)
range遍历string按Unicode字符(rune),而非字节。
按字节遍历
Go
s := "hello"
// 转为字节slice遍历
for i, b := range []byte(s) {
fmt.Println(i, b)
}
遍历channel
接收channel数据
Go
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
// range遍历channel
for v := range ch {
fmt.Println(v)
}
// 1, 2, 3
// channel关闭后自动退出
range遍历channel直到channel关闭。
range返回值对比
| 类型 | 第一个返回值 | 第二个返回值 |
|---|---|---|
| slice/array | 索引int | 元素值 |
| map | 键key | 值value |
| string | 字节索引 | Unicode码点rune |
| channel | 值value | 无(只有值) |
range陷阱
闭包捕获循环变量
Go
// 错误:闭包捕获循环变量
for _, v := range []int{1, 2, 3} {
go func() {
fmt.Println(v) // 可能全打印3
}()
}
// 正确:传递参数
for _, v := range []int{1, 2, 3} {
go func(n int) {
fmt.Println(n)
}(v)
}
遍历时修改集合
Go
// 不推荐:遍历时修改
m := map[string]int{"a": 1}
for k, v := range m {
m["b"] = 2 // 遍历时添加可能导致意外行为
}
// 推荐:遍历后再修改
var toAdd []string
for k, v := range m {
// 收集要添加的
}
for _, k := range toAdd {
m[k] = 2
}
range使用示例
Go
func processAll(nums []int, m map[string]int, s string) {
// slice遍历
for i, v := range nums {
fmt.Printf("slice[%d]=%d\n", i, v)
}
// map遍历
for k, v := range m {
fmt.Printf("map[%s]=%d\n", k, v)
}
// string遍历
for i, c := range s {
fmt.Printf("string[%d]=%c\n", i, c)
}
}
要点总结
- range遍历slice/array返回索引和值
- range遍历map返回键和值,顺序随机
- range遍历string返回字节索引和rune
- range遍历channel返回值,关闭自动退出
- range返回的值是副本,修改不影响原集合
- 用索引修改原slice元素
- map遍历顺序随机,不适合有序场景
- 遍历string按字符而非字节
- 闭包捕获循环变量需传递参数
📝 发现内容有误?点击此处直接编辑