成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go for range 一不小心就掉坑里了

開(kāi)發(fā) 項(xiàng)目管理
for循環(huán)問(wèn)題,在面試中經(jīng)常都會(huì)被問(wèn)到,并且在實(shí)際業(yè)務(wù)項(xiàng)目中也經(jīng)常用到for循環(huán),要是沒(méi)用好,一不下心就掉坑。

前言

為了讓大家更好的理解本期知識(shí)點(diǎn),先介紹以下幾個(gè)知識(shí)點(diǎn):線性結(jié)構(gòu)、非線性結(jié)構(gòu)、循環(huán)、迭代、遍歷、遞歸。

  • 線性結(jié)構(gòu):數(shù)組、隊(duì)列
  • 非線性結(jié)構(gòu):樹(shù)、圖
  • 循環(huán)(loop):最基礎(chǔ)的概念,所有重復(fù)的行為都是循環(huán)
  • 遞歸(recursion):在函數(shù)內(nèi)調(diào)用自身,將復(fù)雜情況逐步轉(zhuǎn)化成基本情況
  • (數(shù)學(xué))迭代(iterate):在多次循環(huán)中逐步接近結(jié)果
  • (編程)迭代(iterate):按順序訪問(wèn)線性結(jié)構(gòu)中的每一項(xiàng)
  • 遍歷(traversal):按規(guī)則訪問(wèn)非線性結(jié)構(gòu)中的每一項(xiàng)

下面會(huì)挑選幾個(gè)經(jīng)典的案例,一塊來(lái)探討下,看看如何避免掉坑,多積累積累采坑經(jīng)驗(yàn)。

1. for+傳值

先來(lái)到開(kāi)胃菜,熱熱身~

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)
}
}

不出意料,輸出結(jié)果為:

李四 => 李四
王五 => 王五
張三 => 張三

這題比較簡(jiǎn)單,就是簡(jiǎn)單的傳值操作,大家應(yīng)該都能答上來(lái)。下面加大難度,改為傳址操作

2. 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)
}
}

好好想想應(yīng)該輸出什么結(jié)果呢?還是跟案例一是一樣的結(jié)果嗎?難道會(huì)有坑?

不出意料,還是出了意外,輸出結(jié)果為:

張三 => 王五
李四 => 王五
王五 => 王五

為什么呢?

  • 首先,關(guān)鍵點(diǎn)在于Go的for循環(huán),對(duì)循環(huán)變量stu?每次是循環(huán)并不是迭代(簡(jiǎn)單的說(shuō),就是對(duì)循環(huán)變量stu只會(huì)做一次聲明和內(nèi)存地址的分配,后面循環(huán)就是不斷更新值);
  • 所以,取址操作 &stu,其實(shí)都是取的同一個(gè)變量的地址,只是值被循環(huán)更新為最后一個(gè)元素的值;
  • 最終,輸出的v.name,都是最后一個(gè)元素的name為王五。

解決方案:

在for循環(huán)中,做同名變量覆蓋stu:=stu(即重新聲明一個(gè)局部變量,做值拷貝,避免相互影響)

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)
}
}

輸出結(jié)果:
張三 => 張三
李四 => 李四
王五 => 王五

3.for+閉包

在for循環(huán)里,做閉包操作,也是很容易掉坑的。看看下面輸出什么?

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

一眼看過(guò)去,感覺(jué)是輸出1 2 3,但實(shí)際會(huì)輸出 3 3 3

為什么呢?

  • 首先,在分析了案例二后,我們知道了Go的for循環(huán)對(duì)循環(huán)變量v,其實(shí)每次是循環(huán)并不是迭代;
  • 然后,閉包=函數(shù)+引用環(huán)境,在同一個(gè)引用環(huán)境下,循環(huán)變量v的值會(huì)被不斷的覆蓋;
  • 所以最終,在打印時(shí),輸出的v,都是最后一個(gè)值3。

解決方案:

和案例二解決方案一樣,是在for循環(huán)中,做同名變量覆蓋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()
}

輸出結(jié)果:
1
2
3

4. for+goroutine

在for循環(huán)里,起goroutine協(xié)程,也是很迷惑很容易掉坑的。看看下面輸出什么?

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()

一眼看過(guò)去,感覺(jué)是會(huì)無(wú)序輸出1 2 3 4 5,但實(shí)際會(huì)輸出 5 5 5 5 5

為什么呢?

  • 首先,要記得Go的for循環(huán)對(duì)循環(huán)變量str,其實(shí)每次是循環(huán)并不是迭代;
  • 然后,main協(xié)程會(huì)和新起的協(xié)程做相互博弈,看誰(shuí)執(zhí)行更快,按這個(gè)案例執(zhí)行情況來(lái)看,main協(xié)程執(zhí)行速度明顯比新起的協(xié)程會(huì)更快,所以str被更新為最后一個(gè)元素值5(備注:并非絕對(duì));
  • 最終,在新起的協(xié)程中,使用str時(shí)值都為5,作為結(jié)果去輸出;
  • 拓展:如果在新起協(xié)程前,sleep個(gè)5s,輸出結(jié)果又會(huì)截然不同,感興趣的同學(xué)可以自行實(shí)驗(yàn)下,然后逐步深入地了解下GMP調(diào)度機(jī)制。

解決方案:

和前面兩個(gè)案例解決方案一樣,是在for循環(huán)中,做同名變量覆蓋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()

輸出結(jié)果:
5
4
2
1
3
注意是1~5無(wú)序輸出

總結(jié)

for循環(huán)中做傳址、閉包、goroutine相關(guān)操作,千萬(wàn)要注意,一不小心就會(huì)很容易掉坑。

使用好同名變量覆蓋v:=v,這個(gè)解決大法,能很便捷的解決這一類問(wèn)題。

本文轉(zhuǎn)載自微信公眾號(hào)「 程序員升級(jí)打怪之旅」,作者「王中陽(yáng)Go」,可以通過(guò)以下二維碼關(guān)注。

轉(zhuǎn)載本文請(qǐng)聯(lián)系「 程序員升級(jí)打怪之旅」公眾號(hào)。

責(zé)任編輯:武曉燕 來(lái)源: 程序員升職加薪之旅
相關(guān)推薦

2021-07-28 05:01:29

Lombok前端測(cè)試

2021-01-05 22:49:37

Python編程語(yǔ)言Java

2020-10-26 08:56:32

技術(shù)總監(jiān)程序員

2025-03-05 00:27:00

2021-04-30 08:21:22

Linux管道設(shè)計(jì)

2024-05-13 07:58:52

開(kāi)源項(xiàng)目PR

2018-01-18 22:26:30

2021-10-07 16:45:06

MySQL數(shù)據(jù)庫(kù)

2021-03-29 18:47:53

APP服務(wù)端通信安全數(shù)據(jù)安全

2021-01-30 09:50:54

MySQL密碼服務(wù)器

2019-09-19 20:47:29

刷臉支付人臉識(shí)別人工智能

2020-10-28 15:07:01

Arthas

2020-09-29 07:44:20

跨域前后端分離插件

2021-01-08 09:36:23

程序員比特幣黑客

2020-11-24 11:30:51

SpringJava代碼

2020-04-30 09:45:41

安卓App小米

2021-03-26 06:14:26

Hashcode項(xiàng)目排查

2022-09-27 18:19:32

Java數(shù)據(jù)結(jié)構(gòu)

2019-07-29 14:38:35

服務(wù)器開(kāi)發(fā)工具

2023-11-17 18:17:33

微信支付V3版本
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲天天干 | 久久涩涩| 毛片一区 | 国精日本亚洲欧州国产中文久久 | 午夜看片 | 欧美成人手机视频 | 成人国产a | 精品一级| 久热爱| 少妇黄色| 久久国产精品一区二区三区 | av片在线播放 | 亚州无限乱码 | 伊人焦久影院 | 国产成人免费视频网站高清观看视频 | 不卡的av在线 | 欧美成人黄色小说 | 懂色av一区二区三区在线播放 | 欧美电影一区 | 伊人伊人伊人 | 91久久爽久久爽爽久久片 | 国产不卡在线观看 | 99re热精品视频| 国产不卡在线播放 | 国产一区二区三区免费 | 欧美一区二区三区免费在线观看 | www.99re| 涩涩99| 二区三区视频 | 亚洲视频欧美视频 | 色资源在线观看 | 狠狠做深爱婷婷综合一区 | 第一色在线| 久久91av| 国产成人综合在线 | 欧美在线a | 999精品网 | 日韩在线视频免费观看 | 在线男人天堂 | 全免费a级毛片免费看视频免 | 精品99在线|