在 Go 语言中,range 是用于遍历集合的关键字,用法简洁灵活。本文将对其用法做总结。
基本语法
range 遍历集合时,会返回 索引(或键) 和 对应的值,语法格式如下:
for 索引/(键, 值) := range 集合 {
// TODO
}
● 若只需要值,可使用匿名变量 _ 忽略索引/键;
● 若只需要索引/键,可省略值的变量。
遍历不同类型的集合
array/slice
● 返回值:索引 和 元素值(值拷贝,修改遍历变量不影响原数组/切片)。
● 示例
nums := []int{1, 2, 3}
for i, v := range nums {
fmt.Printf("索引: %d, 值: %d\n", i, v)
}
// 输出:
// 索引: 0, 值: 1
// 索引: 1, 值: 2
// 索引: 2, 值: 3
string
● 返回值:字节索引 和 Unicode 码点(rune)(字符串按 Unicode 字符遍历,非 ASCII 字符可能占多个字节)。
● 示例
s := "hello 世界"
for i, c := range s {
fmt.Printf("索引: %d, 字符: %c\n", i, c)
}
// 输出(中文占三个字节)
索引: 0, 字符: h
索引: 1, 字符: e
索引: 2, 字符: l
索引: 3, 字符: l
索引: 4, 字符: o
索引: 5, 字符:
索引: 6, 字符: 世
索引: 9, 字符: 界
map
● 返回值:键(key) 和 值(value)(遍历顺序不固定,每次可能不同)。
● 示例
m := map[string]int{"a": 1, "b": 2}
for k, v := range m {
fmt.Printf("键: %s, 值: %d\n", k, v)
}
// 可能输出:
// 键: a, 值: 1
// 键: b, 值: 2
channel
● 返回值:通道中接收的数据(无索引/键,若通道关闭则退出循环)。
● 示例
ch := make(chan int, 2)
ch <- 1
ch <- 2
close(ch)
for v := range ch { // 只返回值
fmt.Println(v)
}
// 输出:
// 1
// 2
用于控制循环次数
// 循环固定次数,返回当前的循环次数
for i := range 5 {
fmt.Println("i = ", i)
}
// 固定循环次数,忽略当前循环次数
for range 5 {
// TODO:
}
注意事项及规避方法
遍历变量的复用
range 遍历中,索引和值的变量是复用的(即每次循环使用同一个内存地址)。若在循环中保存变量地址,可能导致意外结果。
nums := []int{1, 2, 3}
var ps []*int
for _, v := range nums {
// 错误:所有元素都指向同一个v的地址
ps = append(ps, &v)
}
// ps 中的指针可能全指向 3(最后一次循环的值)
解决方法:使用临时变量或直接取原数组地址。
for i := range nums {
// 正确:取原元素地址
ps = append(ps, &nums[i])
}
数组遍历与长度
遍历数组时,range 会按数组的实际长度遍历,而非指针或切片的长度。
arr := [3]int{1, 2, 3}
slice := arr[:2] // 切片长度为2
for i, v := range arr { // 遍历数组,3次循环
fmt.Println(i, v)
}
for i, v := range slice { // 遍历切片,2次循环
fmt.Println(i, v)
}
map遍历的删除与新增
● 遍历中删除已遍历的键:安全,不影响后续遍历。
● 遍历中新增键:新增的键可能被遍历到,也可能不被遍历到(不确定)。
字符串遍历与字节索引
非 ASCII 字符(如中文)在字符串中占多个字节,range 返回的索引是字节索引,而非字符位置。
s := "世界"
// 输出 6(每个中文字符占3字节)
fmt.Println(len(s))
for i, c := range s {
// 索引为 0 和 3
fmt.Printf("索引: %d, 字符: %c\n", i, c)
}

