三分鐘學 Go 語言——range深度解析

小熊最近兩天加班比較嚴重,要處理的事情很多,但是學習的熱情永遠不會減少,前面講述的go語言語法是非常非常簡單的,所以沒有做深入的剖析,後面會從各種角度解析語法,fighting!!

range(範圍)

range 關鍵字在 go 語言中是相當常用好用的語法糖,可以用在 for 循環中迭代 arrayslicemapchannel字符串所有涉及到遍歷輸出的東西。

基本原理

怎麼用?

我們在前一節循環中初次觸及到了 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

  • 遍歷過程中可以適情況放棄接收 indexvalue,可以一定程度上提升性能
  • 遍歷 channel 時,如果 channel 中沒有數據,可能會阻塞
  • 儘量避免遍歷過程中修改原數據

總結

  • range可以用於for 循環中迭代 arrayslicemapchannel字符串所有涉及到遍歷輸出的東西。
  • for-range 的實現實際上是C風格的for循環
  • 使用index,value接收range返回值會發生一次數據拷貝
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章