Golang 語言 For 和 For-range 的區(qū)別
1介紹
在 Golang 語言中,僅有 for 一種循環(huán)語句,但是可以模擬 while (condition) {} 和 while (true) {}。
除此之外,Golang 語言還引入了另外一個關鍵字 range,我們也可以配合 for 關鍵字,使用 for-range循環(huán)遍歷數(shù)據(jù)。
本文我們介紹一下使用 for 和 for-range 循環(huán)遍歷數(shù)據(jù)的區(qū)別。
2區(qū)別
在 Golang 語言程序開發(fā)中,經(jīng)常會使用循環(huán)遍歷數(shù)據(jù),因為 Golang 語言提供了 for-range 范圍遍歷,可以更加方便的幫助我們循環(huán)遍歷數(shù)據(jù),比如數(shù)據(jù)和數(shù)組指針、切片、字符串、映射和通道,所以我們比較喜歡使用 for-range 范圍遍歷替代 for 普通遍歷。
實際上,使用范圍遍歷 for-range 和使用普通遍歷 for 是有區(qū)別的,因為范圍遍歷會在遍歷之前,先拷貝一份被遍歷的數(shù)據(jù),然后遍歷拷貝的數(shù)據(jù)。
3踩「坑」
在 Golang 語言程序開發(fā)中,因為不了解使用范圍遍歷切片和使用普通遍歷切片的區(qū)別,新手經(jīng)常會在此踩“坑”。
范圍遍歷切片
- func main() {
- s := []int{0, 1}
- for _, v := range s {
- s = append(s, v)
- }
- fmt.Printf("s=%v\n", s)
- }
輸出結(jié)果:
- s=[0 1 0 1]
普通遍歷切片
- func main() {
- s := []int{0, 1}
- for i := 0; i < len(s); i++ {
- s = append(s, s[i])
- }
- fmt.Printf("s=%v\n", s)
- }
輸出結(jié)果:
閱讀上面這段代碼,我們分別使用范圍遍歷和普通遍歷,循環(huán)遍歷同一個切片。范圍遍歷可以輸出結(jié)果,而普通遍歷是無限循環(huán),無法輸出結(jié)果。
原因是范圍遍歷在開始遍歷數(shù)據(jù)之前,會先拷貝一份被遍歷的數(shù)據(jù),所以在遍歷過程中去修改被遍歷的數(shù)據(jù),只是修改拷貝的數(shù)據(jù),不會影響到原數(shù)據(jù)。
在該示例代碼中,我們在循環(huán)過程中,不斷追加數(shù)據(jù)到切片,原以為范圍遍歷也可以像普通遍歷那樣,無限循環(huán),但是實際上結(jié)果并非所想。
同時,因為范圍遍歷會拷貝被遍歷數(shù)據(jù),所以在需要遍歷內(nèi)存占用比較大的數(shù)組時,建議使用普通遍歷,如果必須使用范圍遍歷,我們可以遍歷數(shù)組的地址或先將數(shù)組轉(zhuǎn)換為切片,然后再遍歷。
4總結(jié)
本文我們介紹了在 Golang 語言中,循環(huán)遍歷數(shù)據(jù)的兩種方式之間有什么區(qū)別,其中最大的區(qū)別,也是最容易踩“坑”的地方,就是范圍遍歷會在遍歷之前,先拷貝一份被遍歷的數(shù)據(jù),然后遍歷拷貝的數(shù)據(jù)。
如果讀者朋友們不清楚這個區(qū)別,在程序開發(fā)中,肯定會遇到這樣或那樣的“坑”,而且還很難發(fā)現(xiàn)和繞過。
除了文中列舉的示例,相信讀者朋友們肯定還有很多其他踩“坑”場景,歡迎在留言區(qū)與大家分享。
參考資料:
https://gobyexample.com/range
https://go.dev/tour/moretypes/16
https://go.dev/ref/spec#For_statements
https://www.geeksforgeeks.org/range-keyword-in-golang/
https://golangbyexample.com/understand-for-range-loop-golang/
https://www.educative.io/edpresso/what-is-the-for-range-loop-in-golang