亲宝软件园·资讯

展开

Go语言中for循环的经典案例分析

劲仔Go 人气:0

前言

for循环问题,在面试中经常都会被问到,并且在实际业务项目中也经常用到for循环,要是没用好,一不下心就掉坑。

下面会挑选几个经典的案例,一块来探讨下,看看如何避免掉坑,多积累积累采坑经验。

案例一:for+传值

先来到开胃菜,热热身~

type student struct {
  name string
  age  int
}

func main() {
  m := make(map[string]student)
  stus := []student{
    {name: "张三", age: 18},
    {name: "李四", age: 23},
    {name: "王五", age: 26},
  }
  for _, stu := range stus {
    m[stu.name] = stu
  }
  for k, v := range m {
    fmt.Println(k, "=>", v.name)
  }
}

不出意料,输出结果为

李四 => 李四
王五 => 王五
张三 => 张三

这题比较简单,就是简单的传值操作,大家应该都能答上来。下面加大难度,改为传址操作

案例二:for+传址

将案例一改为传址操作

type student struct {
  name string
  age  int
}

func main() {
  m := make(map[string]*student)
  stus := []student{
    {name: "张三", age: 18},
    {name: "李四", age: 23},
    {name: "王五", age: 26},
  }
  for _, stu := range stus {
    m[stu.name] = &stu
  }
  for k, v := range m {
    fmt.Println(k, "=>", v.name)
  }
}

好好想想应该输出什么结果呢?还是跟案例一是一样的结果吗?难道会有坑?

不出意料,还是出了意外,输出结果为

张三 => 王五
李四 => 王五
王五 => 王五

为什么呢?

解决方案

在for循环中,做同名变量覆盖stu:=stu(即重新声明一个局部变量,做值拷贝,避免相互影响)

type student struct {
  name string
  age  int
}

func main() {
  m := make(map[string]*student)
  stus := []student{
    {name: "张三", age: 18},
    {name: "李四", age: 23},
    {name: "王五", age: 26},
  }
  for _, stu := range stus {
    stu := stu  //同名变量覆盖
    m[stu.name] = &stu
  }
  for k, v := range m {
    fmt.Println(k, "=>", v.name)
  }
}

输出结果:

张三 => 张三
李四 => 李四
王五 => 王五

案例三:for+闭包

在for循环里,做闭包操作,也是很容易掉坑的。看看下面输出什么?

var prints []func()
for _, v := range []int{1, 2, 3} {
  prints = append(prints, func() { fmt.Println(v) })
}
for _, print := range prints {
  print()
}

一眼看过去,感觉是输出1 2 3,但实际会输出 3 3 3

为什么呢?

解决方案

和案例二解决方案一样,是在for循环中,做同名变量覆盖v:=v

var prints []func()
for _, v := range []int{1, 2, 3} {
  v := v //同名变量覆盖  
  prints = append(prints, func() { fmt.Println(v) })
}
for _, print := range prints {
  print()
}

输出结果:

1
2
3

案例四:for+goroutine

在for循环里,起goroutine协程,也是很迷惑很容易掉坑的。看看下面输出什么?

var wg sync.WaitGroup
strs := []string{"1", "2", "3", "4", "5"}
for _, str := range strs {
  wg.Add(1)
  go func() {
    defer wg.Done()
    fmt.Println(str)
  }()
}
wg.Wait()

一眼看过去,感觉是会无序输出1 2 3 4 5,但实际会输出 5 5 5 5 5

为什么呢?

解决方案

和前面两个案例解决方案一样,是在for循环中,做同名变量覆盖str:=str

var wg sync.WaitGroup
strs := []string{"1", "2", "3", "4", "5"}
for _, str := range strs {
  str := str //同名变量覆盖
  wg.Add(1)
  go func() {
    defer wg.Done()
    fmt.Println(str)
  }()
}
wg.Wait()

输出结果:

5
4
2
1
3

注意是1~5无序输出

总结

for循环中做传址、闭包、goroutine相关操作,千万要注意,一不小心就会很容易掉坑。

加载全部内容

相关教程
猜你喜欢
用户评论