小熊最近兩天加班比較嚴重,要處理的事情很多,但是學習的熱情永遠不會減少,前面講述的go
語言語法是非常非常簡單的,所以沒有做深入的剖析,後面會從各種角度解析語法,fighting!!
range(範圍)
range
關鍵字在 go
語言中是相當常用好用的語法糖,可以用在 for
循環中迭代 array
、slice
、map
、channel
、字符串
所有涉及到遍歷輸出的東西。
基本原理
怎麼用?
我們在前一節循環中初次觸及到了 range
,也知道他可以省略key
,或者省略value
來循環遍歷的特性,但是這種特性要結合實際情況考量該用哪一個。
切片迭代
nums := []int{1, 2, 3}
for k, v := range nums {
fmt.Printf("key: %v , value: %v \n", k, v)
}
這和迭代方式非常適合用for-range
語句,如果減少賦值,直接索引num[key]
可以減少損耗。如下
for k, _:= range nums {
map
迭代
注意,從 Go1
開始,遍歷的起始節點就是隨機了。
kvs := map[string]string{
"a":"a",
"b":"b",
}
for k, v := range kvs {
fmt.Printf("key: %v , value: %v \n", k, v)
}
函數中for-range
語句中只獲取 key
值,然後跟據 key
值獲取 value
值,雖然看似減少了一次賦值,但通過 key
值查找 value
值的性能消耗可能高於賦值消耗。
所以能否優化取決於 map 所存儲數據結構特徵、結合實際情況進行。
字符串迭代(一個一個的輸出字符)
for k,v := range "hello"{
//注意這裏單個字符輸出的是ASCII碼,
//用 %c 代表輸出字符
fmt.Printf("key: %v , value: %c
\n", k, v)
}
channel (如果不會可以先 mark 下,詳細參考後續:go 的併發特性章節)
ch := make(chan int, 10)
ch <- 11
ch <- 12
close(ch) // 不用的時候記得關掉,不關掉又沒有另一個goroutine存在會死鎖哦,可以註釋掉這一句體驗死鎖
for x := range ch {
fmt.Println(x)
}
結構體
tmp := []struct{
int
string
}{
{1, "a"},
{2, "b"},
}
for k,v := range tmp{
fmt.Printf("k:%v, v:%v \n",k,v)
}
注意:由於循環開始前循環次數就已經確定了,所以循環過程中新添加的元素是沒辦法遍歷到的。
有可能會遇到的坑!
由於range
遍歷時value
是值的拷貝,如果這個時候遍歷上一節聲明的結構體時,修改value
,原結構體不會發生任何變化!
for _,v := range tmp{
v.a = 2
}
兩次輸出一致
k:0, v:{1 a}
k:1, v:{2 b}
k:0, v:{1 a}
k:1, v:{2 b}
編程 Tips
- 遍歷過程中可以適情況放棄接收
index
或value
,可以一定程度上提升性能 - 遍歷
channel
時,如果channel
中沒有數據,可能會阻塞 - 儘量避免遍歷過程中修改原數據
總結
range
可以用於for
循環中迭代array
、slice
、map
、channel
、字符串
所有涉及到遍歷輸出的東西。for-range
的實現實際上是C
風格的for
循環- 使用
index
,value
接收range
返回值會發生一次數據拷貝