• 【Golang】由 for range 循环引出的一些思考


    一、背景

    今天看到组内项目中有这样一段代码 ,第一反应是局部变量 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 
  • 相关阅读:
    Redis实战——redis主从备份和哨兵模式实践
    Shiro的Subject和Sessoin的创建
    Shiro配置cookie以及共享Session和Session失效问题
    Shiro的校验Session是否过期处理的过程
    Maven父级pom.xml配置文件
    我的Linux之路——虚拟机linux与主机之间的文件传送
    Redis实战——安装问题汇总
    我的Linux之路——实现虚拟机VMware上linux与windows互相复制与粘贴
    Redis实战——phpredis扩展安装
    推荐一个面向对象的javascript框架mootools
  • 原文地址:https://www.cnblogs.com/Lily-nercel/p/14651111.html
Copyright © 2020-2023  润新知