Golang如何使用range遍历集合_数组切片map遍历技巧

1次阅读

go中range遍历切片、数组、map时,v均为值拷贝,修改v不影响原数据;需通过索引(如nums[i])或键(如m[k])修改;循环变量v复用易致goroutine或取址错误。

Golang如何使用range遍历集合_数组切片map遍历技巧

range 遍历切片时,v 是副本,修改 v 不影响原切片元素

Go 中 range 遍历切片([]T)时,第二个变量 v 是每个元素的**值拷贝**,不是引用。直接修改 v 不会改变底层数组内容。

常见错误写法:

nums := []int{1, 2, 3} for _, v := range nums {     v *= 2 // 这里改的是 v 的副本,nums 不变 } // nums 仍是 [1, 2, 3]

正确做法是用索引访问并修改:

  • 需要修改原切片 → 用 i 索引: nums[i] *= 2
  • 只读遍历 → v 更安全、更清晰
  • 若切片元素是结构体指针[]*T),v 是指针副本,仍可间接修改字段(但别误以为是结构体本身被复制)

range 遍历 map 时,遍历顺序不保证,且 v 同样是值拷贝

Go 的 map 是哈希表实现,range 遍历顺序**随机且每次不同**(从 Go 1.0 起故意设计,防依赖顺序的 bug)。同时,v 是每个 value 的副本。

立即学习go语言免费学习笔记(深入)”;

示例:

m := map[String]int{"a": 1, "b": 2, "c": 3} for k, v := range m {     fmt.Println(k, v) // 每次运行输出顺序可能不同 }

如果需要固定顺序(比如按 key 排序输出):

  • 先收集所有 key 到切片:keys := make([]string, 0, len(m))
  • sort.Strings(keys)
  • 再遍历 keys,通过 m[k] 取值

注意:v 是副本,修改 v 不影响 m[k];要更新 map 值,必须写 m[k] = newV

range 遍历数组时,len 和 cap 固定,但 range 仍按元素值拷贝

数组(如 [3]int)是值类型range 遍历时同样把每个元素以值方式赋给 v。虽然数组长度不可变,但遍历行为和切片一致。

关键区别在于:传参或赋值时,数组会整体拷贝;而切片只拷贝 header(指针+长度+容量)。

  • 遍历 [5]int 时,vint 类型,不是引用
  • 想修改原数组 → 必须用索引:a[i] = ...
  • range 遍历数组比切片稍快(无 header 解引用开销),但差异通常可忽略

for-range 中的变量复用陷阱:循环变量地址相同

这是最隐蔽也最常踩的坑:在循环中启动 goroutine 或保存变量地址时,所有迭代共享同一个 v 变量内存位置。

错误示例(闭包捕获的是同一个 v):

values := []string{"a", "b", "c"} for _, v := range values {     go func() {         fmt.Println(v) // 全部打印 "c"(最后一次赋值)     }() } // 正确做法:显式传参 for _, v := range values {     go func(val string) {         fmt.Println(val)     }(v) }

同理,往切片中 append 取地址也会出错:

  • ptrs = append(ptrs, &v) → 所有指针指向同一内存
  • ptrs = append(ptrs, &values[i]) 或先拷贝:val := v; ptrs = append(ptrs, &val)

这个陷阱和类型无关(切片/map/数组都存在),本质是 Go 循环变量复用机制决定的 —— 它不是每次迭代新建变量。

text=ZqhQzanResources