一、背景
今天看到组内项目中有这样一段代码 ,第一反应是局部变量 index 太奇怪了,当然也不能说这样写是错的,只是个人强迫症看着很难受...
1 for _, index := range entities { 2 key := index.TemplateId
刚好借此机会,梳理下 Go 的 for range 循环及相关知识点,做个小小总结。
二、知识点梳理
1,使用 Go 的 for range 进行循环时,range 关键字返回有两个变量,第一个是索引index,第二个是值 value,所以上述的代码我个人觉得 index 替换成 value 会更合适。
2,for range 进行循环时,返回的变量 v 实际是copy出来的副本值,我理解以下两段代码是等价的:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 for _, v := range s { 6 } 7 }
1 func ForSlice() { 2 s := []int{1, 2, 3} 3 var v int 4 var l = len(s) 5 for i := 0; i < l; i++ { 6 v = s[i] 9 } 10 }
这意味着在 range 循环里面直接修改 v 值,是无法达到修改原 slice 值的效果的,示例如下:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 fmt.Printf("修改前的s:%v ", s) 4 for _, v := range s { 5 v++ 6 } 7 fmt.Printf("修改后的s:%v ", s) 8 }
输出:
修改前的s:[1 2 3]
修改后的s:[1 2 3]
在日常项目中,如果需要修改原slice值要怎么做呢?可以通过直接修改 s[i] 值的方式进行修改:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 fmt.Printf("修改前的s:%v ", s) 4 for i := range s { 5 s[i]++ 6 } 7 fmt.Printf("修改后的s:%v ", s) 8 }
输出:
修改前的s:[1 2 3]
修改后的s:[2 3 4]
3,for range 循环之前会先获取长度,所以下面这段代码不会出现死循环情况:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 for _, v := range s { 4 fmt.Println(v) 5 s = append(s, v) 6 } 7 fmt.Println("循环结束") 8 fmt.Printf("新的s是:%v", s) 9 }
1
2
3
循环结束
新的s是:[1 2 3 1 2 3]
4,还有一个是看网上大佬们讨论比较多的,关于指针数据的问题:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 newSlice := []*int{} 4 for _, v1 := range s { 5 newSlice = append(newSlice, &v1) 6 } 7 for _, v2 := range newSlice { 8 fmt.Printf("newSlice是:%v ", *v2) 9 } 10 }
newSlice是:3 newSlice是:3 newSlice是:3
这个问题从 2 中代码也可以很好的理解,因为变量 v1 的地址是没有变化的,只是在每次循环不断的重新赋值,即 v2 指向的是同一个地址,值是循环最后一个值。
如何达到存储 1,2,3 这样的预期呢,有以下两种方式:
1)增加一个中间变量 temp 的方式,当然这种需要多开辟内存空间:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 newSlice := []*int{} 4 for _, v := range s { 5 temp := v 6 newSlice = append(newSlice, &temp) 7 } 8 for _, v := range newSlice { 9 fmt.Printf("newSlice是:%v ", *v) 10 } 11 }
newSlice是:1 newSlice是:2 newSlice是:3
2)直接取原始 slice value 的地址,这种比较推荐可以减少内存空间:
1 func RangeForSlice() { 2 s := []int{1, 2, 3} 3 newSlice := []*int{} 4 for i := range s { 5 newSlice = append(newSlice, &s[i]) 6 } 7 for _, v := range newSlice { 8 fmt.Printf("newSlice是:%v ", *v) 9 } 10 }
newSlice是:1 newSlice是:2 newSlice是:3