# Day09 Go繼續開車-基本組合型別(array, slice) 組合(複合)型別,顧名思義是建構在基礎型別之上,把上一章節所教的型別組合在一起。 ## 陣列 #### 【Array 陣列】 陣列的長度在宣告之後無法改變 https://play.golang.org/p/PkeZt8WRY5B ```go var a [5]int a[0] = 10 a[1] = 100 a[2] = 1000 fmt.Println(a) ``` 值也可以一開始就給,可部分宣告值,沒值的預設為`零值` ```go b := [5]int{1, 2, 3} //單行宣告 fmt.Println(b) c := [5]int{ 10, 20, 30, 55, //使用多行宣告的話,最後一個元素要逗號 } fmt.Println(c[1:3]) d := [...]int{4, 6, 8} //用...省略符號,讓go判斷長度 fmt.Println(d) /* result: [10 100 1000 0 0] [1 2 3 0 0] [20 30] [4 6 8] */ ``` #### 【微坑注意】 ```go e:= []int{1,2} ``` 這種`[]`內沒任何東西的宣告是屬於`Slice`, 而不是省略長度的`Array` ## 切片 #### 【Slice 切片】 切片是啥?肉切片?組織切片? 這名詞真的是學Go時第一次看到,還能不潮嗎? `陣列(array)`使用上非常方便, 但每次都得預先宣告一個長度、撥一段記憶體空間給陣列使用。 為了更加靈活,Google說這邊有一個物件叫 `Slice`,無腦用就對了。 官方文檔的介紹: > An array has a fixed size. > A slice, on the other hand, is a dynamically-sized, > flexible view into the elements of an array. 宣告時中括號[]裡為空 把`Slice`拆開來看,這玩意包含了三樣東西: 1. 長度 `len` ,舉個例子,就是現在的長度 ~~5 cm~~ 2. 容量 `cap` ,最大能容納的長度 ~~30cm~~ 3. 指針 `ptr` ,透過指針 我們可以與別人共用同一個地方 用文字敘述 好像有點越描越黑的感覺,我們還是趕緊來看範例... 宣告`Slice`的方法之一: > **Variable := make([]Type, Len, Cap)** >> b := make([]int, 5, 10) 宣告`變數b`為`len:5`、`cap:10`~~的男人~~ 的整數切片。 以下是幾種宣告`Slice`的方法: https://play.golang.org/p/p9isnfQh1wW ```go a := make([]int, 10) //設定 len:10。現在長度10了,容量雖然沒給,但最大容納長度當然不可能小於10吧,所以就是10了 fmt.Println(a, len(a), cap(a), len(a) == 0, a == nil) b := make([]int, 5, 10) //設定 len:5、cap:10 fmt.Println(b, len(b), cap(b), len(b) == 0, b == nil) var c = []int{} //初始化slice fmt.Println(c, len(c), cap(c), len(c) == 0, c == nil) var d []int //尚未實體化,此時等於nil fmt.Println(d, len(d), cap(d), len(d) == 0, d == nil) e := []string{"youtube.com", "facebook.com"} //直接賦值 fmt.Println(e, len(e), cap(e), len(e) == 0, e == nil) /* result: [0 0 0 0 0 0 0 0 0 0] 10 10 false false [0 0 0 0 0] 5 10 false false [] 0 0 true false [] 0 0 true true [youtube.com facebook.com] */ ``` 初始化沒給值的話都是`零值`。 Slice並不是真正存值,而是透過指針指到更下面的地方,某個陣列 這也是為什麼說可以跟別人共用的原因。 如果SliceA跟SliceB都是一樣的值,底層陣列只要一份資料就好,夠省空間吧! ![Pointer](https://i.imgur.com/WskKaXf.png) ## 操作 Slice #### 【Append】 通常會用`append`這個方法來操作`Slice` ```go x := []int{1, 2, 3} x = append(x, 4, 5, 6) fmt.Println(x) /* result: [1 2 3 4 5 6] */ ``` 一開始學到`容量cap`時,我也覺得這功能有點雞肋。 明明`容量cap`不夠時、`長度len`超過`容量cap`的時候, 再來個`append`就好,`容量cap`會自動擴充啊! 不就等於我設定了一個我可以隨時打破的規定,那我要這功能幹嘛??? `Slice`一切乍看之下都非常的美滿,但事情總是發生的令人措手不及...來繼續往下。 ## Slice【坑很多】 一騎來看看花生什麼杯具了 ```go a := make([]int, 0, 10) //給足夠容量 b := append(a, 1, 2, 3) _ = append(a, 99, 88, 77) fmt.Println(b) //-------- aa := make([]int, 0, 2) //給超小容量 bb := append(aa, 1, 2, 3) _ = append(aa, 99, 88, 77) fmt.Println(bb) /* result: [99 88 77] [1 2 3] */ ``` 拿你口雷? 在`足夠容量`中沒發生災情,但是在`超小容量`中卻因為後來的 `_` 做了 `append` , 導致我的`aa`中的值也被改掉了? 沒綽,這就是可以看到`pointer`的地方,原因在於底層的陣列被後來的`_`蓋掉了。