一、前言
學過 Go 的都知道在 Go 語言中有四種復合資料型別:陣列、切片(Slice)、哈希表(Map)和結構體(Struct),而很多 Go 初學者也很容易把陣列和切片弄混淆,所以要怎么把這兩個資料型別分清楚呢?

二、陣列
1.簡介
陣列是聚合型別,是一組同型別資料的集合,通過從0開始的下標索引訪問元素值,在 Go 語言中,陣列是值型別,這就意味著當你將一個陣列賦值給另一個陣列的時候,實際上是將這個陣列拷貝了一份,
陣列的宣告語法為:
var 陣列變數名 [元素數量]Type
語法說明如下所示:
- 陣列變數名:陣列宣告及使用時的變數名;
- 元素數量:陣列的元素數量,可以是一個運算式,但最終計算的結果必須是整型數值;
- Type:可以是任意基本型別,包括陣列本身,型別為陣列本身時,可以實作多維陣列,
2.初始化
默認情況下,陣列的每個元素都被初始化為元素型別對應的零值,對于數字型別來說就是0,在陣列字面值中,如果在陣列的長度位置出現的是“...”省略號,則表示陣列的長度是根據初始化值的個數來計算,示例如下:
1 var a [3]int 2 a[0] = 1 3 b := [2]int{1, 2} 4 c := [...]int{3, 5, 7} 5 fmt.Println(a) // [1 0 0] 6 fmt.Println(b) // [1 2] 7 fmt.Println(c) // [3 5 7]
3.遍歷陣列
陣列通過下標訪問元素,可修改其元素值,陣列的遍歷通過 for 回圈實作:
1 arr := [3]int{2, 4, 6} 2 for i := 0; i < 3; i++ { 3 fmt.Printf("%d ", arr[i]) 4 } // 2 4 6 5 fmt.Println() 6 for _, v := range arr { 7 fmt.Printf("%d ", v) 8 } // 2 4 6
三、切片
1.簡介
陣列的長度不可改變,在一定場合下就不太適用了,Go 語言則提供了一種可以動態擴容的資料型別--切片(Slice),一個切片型別通常會寫作 []T,其中 T 代表切片中元素的資料型別,切片的語法和陣列類似,只是沒有固定長度,
2.區別
切片和陣列有如下區別:
1)和陣列相比,切片除了有長度(len),還有容量(cap),容量指切片當前可容納元素的最大數量,
2)陣列是值型別,切片是參考型別,
值型別和參考型別有什么區別呢?在傳遞引數時,如果是值型別,對引數修改不會對原來的變數產生影響,但若是參考傳遞,對引數的修改也會影響到原始資料,示例如下:
1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 func change(a [3]int, s []int) { 8 a[0] += 1 9 s[0] += 1 10 s = append(s, 9) 11 } 12 13 func main() { 14 arr := [3]int{2, 4, 6} 15 sli := []int{3, 5, 7} 16 change(arr, sli) 17 fmt.Println(arr) // [2 4 6] 18 fmt.Println(sli) // [4 5 7] 19 }
在示例中,分別對陣列 arr 和切片 sli 的第一個元素進行了+1操作,但從列印結果可以看出來只有切片的資料被修改了,而對陣列的修改并沒有改變原始資料,那為什么最后 sli 的結果不是 [4 5 7 9]呢?這是因為 append() 實際上是將切片 sli 復制了一份然后賦值給了 s,已經是一份新的資料了,也就不會對 sli 產生影響了,
3.初始化
切片的初始化可以通過陣列來實作,也可以通過內置函式 make() 來實作,在使用 make() 方法時還可以設定切片的容量,在追加元素時,若切片的容量不足,則會按切片的長度的二倍進行擴容,示例如下:
1 arr := [5]int{1, 2, 3, 4, 5} 2 s1 := arr[2:] 3 fmt.Println(s1) // [3 4 5] 4 s2 := arr[:] 5 fmt.Println(s2) // [1 2 3 4 5] 6 s3 := make([]int, 3) 7 s3[0], s3[1], s3[2] = 2, 4, 6 8 fmt.Println(s3) // [2 4 6]
4.追加元素
在 Go 語言中有一個內置函式 append(),查看原始碼發現它是這么定義的:
func append(slice []Type, elems ...Type) []Type
內置的 append() 函式用于向 slice 追加元素,示例為:
1 arr := [5]int{1, 2, 3, 4, 5} 2 var sli []int 3 for _, v := range arr { 4 sli = append(sli, v) 5 } 6 fmt.Println(sli) // [1 2 3 4 5]
細心的人會發現原始碼中寫的是 elems,這是不是就意味著可以一次添加多個元素呢?試一試:
1 var sli []int 2 sli = append(sli, 1, 2, 3) 3 fmt.Println(sli) // [1 2 3]
例子很簡單,append() 使用起來也很方便,但問題是如果要添加的元素數量超過了切片的容量,又會發生什么情況呢?看下面的例子:
1 var y []int 2 for i := 0; i < 10; i++ { 3 y = append(y, i) 4 fmt.Printf("%d cap=%d %v\n", i, cap(y), y) 5 }
這幾行代碼的運行結果為:
0 cap=1 [0]
1 cap=2 [0 1]
2 cap=4 [0 1 2]
3 cap=4 [0 1 2 3]
4 cap=8 [0 1 2 3 4]
5 cap=8 [0 1 2 3 4 5]
6 cap=8 [0 1 2 3 4 5 6]
7 cap=8 [0 1 2 3 4 5 6 7]
8 cap=16 [0 1 2 3 4 5 6 7 8]
9 cap=16 [0 1 2 3 4 5 6 7 8 9]
可以發現切片的容量從1慢慢增加為2、4、8、16,也就是說在使用 append 將元素添加至切片時,如果超出了容量,將會回傳一個容量二倍與當前切片的切片,
5.切片拷貝
在 Go 語言中,切片的拷貝使用內置函式 copy() 來實作,可以放心的是,切片拷貝是深拷貝,不用像 Python 中糾結深淺拷貝真的很幸福呢!只不過拷貝的時候需要確保目的切片有足夠的容量,否則會拷貝,示例如下:
1 sli := []int{3, 5, 7} 2 res := make([]int, 5) 3 copy(res, sli) 4 fmt.Println(res) // [3 5 7 0 0] 5 fmt.Println(&sli[0], &res[0]) // 0xc000012340 0xc00000c3c0 6 var s []int 7 copy(s, sli) 8 fmt.Println(s) // []
這里 s 列印出來是空的,是由于 s 在初始化的時候沒有分配記憶體空間,copy() 也不會為 s 分配空間,所以 sli 中的元素也就無法拷貝到 s 中了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/55012.html
標籤:Go
上一篇:關于C++編程
下一篇:求哥哥們幫幫我
