Go語言中for循環(huán)的經(jīng)典案例分析
前言
for循環(huán)問題,在面試中經(jīng)常都會被問到,并且在實際業(yè)務項目中也經(jīng)常用到for循環(huán),要是沒用好,一不下心就掉坑。
下面會挑選幾個經(jīng)典的案例,一塊來探討下,看看如何避免掉坑,多積累積累采坑經(jīng)驗。
案例一: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) } }
不出意料,輸出結(jié)果為:
李四 => 李四
王五 => 王五
張三 => 張三
這題比較簡單,就是簡單的傳值操作,大家應該都能答上來。下面加大難度,改為傳址操作
案例二: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) } }
好好想想應該輸出什么結(jié)果呢?還是跟案例一是一樣的結(jié)果嗎?難道會有坑?
不出意料,還是出了意外,輸出結(jié)果為:
張三 => 王五
李四 => 王五
王五 => 王五
為什么呢?
- 首先,關(guān)鍵點在于Go的for循環(huán),對
循環(huán)變量stu
每次是循環(huán)并不是迭代(簡單的說,就是對循環(huán)變量stu
只會做一次聲明和內(nèi)存地址的分配,后面循環(huán)就是不斷更新值); - 所以,取址操作
&stu
,其實都是取的同一個變量的地址,只是值被循環(huán)更新為最后一個元素的值; - 最終,輸出的
v.name
,都是最后一個元素的name為王五
。
解決方案:
在for循環(huán)中,做同名變量覆蓋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) } }
輸出結(jié)果:
張三 => 張三
李四 => 李四
王五 => 王五
案例三:for+閉包
在for循環(huán)里,做閉包操作,也是很容易掉坑的??纯聪旅孑敵鍪裁矗?/p>
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
為什么呢?
- 首先,在分析了案例二后,我們知道了Go的for循環(huán)對循環(huán)變量v,其實每次是循環(huán)并不是迭代;
- 然后,閉包=函數(shù)+引用環(huán)境,在同一個引用環(huán)境下,循環(huán)變量v的值會被不斷的覆蓋;
- 所以最終,在打印時,輸出的v,都是最后一個值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
案例四: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()
一眼看過去,感覺是會無序輸出1 2 3 4 5,但實際會輸出 5 5 5 5 5
為什么呢?
- 首先,要記得Go的for循環(huán)對循環(huán)變量str,其實每次是循環(huán)并不是迭代;
- 然后,main協(xié)程會和新起的協(xié)程做相互博弈,看誰執(zhí)行更快,按這個案例執(zhí)行情況來看,main協(xié)程執(zhí)行速度明顯比新起的協(xié)程會更快,所以str被更新為最后一個元素值5(備注:并非絕對);
- 最終,在新起的協(xié)程中,使用str時值都為5,作為結(jié)果去輸出;
- 拓展:如果在新起協(xié)程前,sleep個5s,輸出結(jié)果又會截然不同,感興趣的同學可以自行實驗下,然后逐步深入地了解下GMP調(diào)度機制。
解決方案:
和前面兩個案例解決方案一樣,是在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無序輸出
總結(jié)
for循環(huán)中做傳址、閉包、goroutine相關(guān)操作,千萬要注意,一不小心就會很容易掉坑。
到此這篇關(guān)于Go語言中for循環(huán)的經(jīng)典案例分析的文章就介紹到這了,更多相關(guān)Go for循環(huán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
安裝GoLang環(huán)境和開發(fā)工具的圖文教程
Go是一門由Google開發(fā)的編程語言,GoLand的安裝非常簡單,本文主要介紹了安裝GoLang環(huán)境和開發(fā)工具的圖文教程,具有一定的參考價值,感興趣的可以了解一下2023-09-09使用Go語言創(chuàng)建error的幾種方式小結(jié)
Go語言函數(shù)(或方法)是支持多個返回值的,因此在Go語言的編程哲學中,函數(shù)的返回值的最后一個通常都是error類型,所以本文給大家介紹了使用Go語言創(chuàng)建error的幾種方式小結(jié),文中通過代碼示例講解的非常詳細,需要的朋友可以參考下2024-01-01Go框架三件套Gorm?Kitex?Hertz基本用法與常見API講解
這篇文章主要為大家介紹了Go框架三件套Gorm?Kitex?Hertz的基本用法與常見API講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>2023-02-02Go type關(guān)鍵字(類型定義與類型別名的使用差異)用法實例探究
這篇文章主要為大家介紹了Go type關(guān)鍵字(類型定義與類型別名的使用差異)用法實例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01Redis?BloomFilter布隆過濾器原理與實現(xiàn)
你在開發(fā)或者面試過程中,有沒有遇到過?海量數(shù)據(jù)需要查重,緩存穿透怎么避免等等這樣的問題呢?下面這個東西超棒,好好了解下,面試過關(guān)斬將,凸顯你的不一樣2022-10-10執(zhí)行g(shù)o?vendor第三方包版本沖突問題解決
這篇文章主要為大家介紹了執(zhí)行g(shù)o?vendor時,第三方包go版本沖突問題的解決方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07