寫在前面
前面的文章介紹了Go的一些基本類型,本文開始涉及Go的一些容器類型,它們都是可以包含多個元素的數據結構,如數組、切片、map
數組
數組是具有相同類型且長度固定的一組元素集合,定義的格式:var 數組名 [數組長度]數組元素類型
,下面聲明一個長度爲5的int型數組arr
var arr [5]int
- 數組元素可以是任意的原始類型,例如整型、字符串等,一個數組內所有元素的類型必須是相同的
- 數組的長度必須是一個常量表達式,且是一個非負整數,特別注意數組的長度也是數組類型的一部分,因此不同長度的數組是不同的類型。數組的長度在聲明時就要給出。獲取數組長度用內置函數
len(arr)
- 當聲明數組時所有的元素都會被初始化爲默認的類型零值,我們也可以在聲明數組的同時進行初始化
還可以在初始化時僅初始化指定元素var arr = [5]int{1, 2, 3, 4, 5} a := [3]int{1, 2, 3}
b := [3]int{1 : 2, 2 : 3} //初始化索引爲1的值爲2,索引爲2的值爲3
- 如果聲明數組時不想直接寫長度,可以用
...
代替,編譯器會自動生成滿足最低長度要求的數組c := [...]int{4 : 1} //因爲我們指定了索引爲4的值爲1,因此數值至少包含5個元素,這裏將生成長度爲5的數組
- 通過
for
循環來遍歷操作數組var arr [5]int for i := 0; i < len(arr); i++ { arr[i] = i * 2 } for i, v := range arr { fmt.Println("index: ", i, "value: ", v) }
- 數組是一種值類型(不像C/C++那樣是指向首元素的指針),可以通過
new
來創建數組,不過它返回的是指向該數組的指針
注意區分指向數組的指針和指針數組d := new([4]int) fmt.Println(d) //&[0,0,0,0] fmt.Println(*d) //[0,0,0,0]
e := [5]int{1, 2, 3, 4, 5} var p *[5]int = &e //p爲指向數組e的指針 &[1,2,3,4,5] x, y := 1, 2 z := [2]*int{&x,&y} //z是一個指針數組
- 多維數組
h := [2][3]int{ {1, 1, 1}, {2, 2, 2} }
- 數組可以使用
==
或!=
來比較f := [2]int{1, 2} g := [2]int{1, 2} fmt.Println(f == g) //true
切片
切片(slice)是對某個數組的一段連續片段的引用(該數組我們稱爲相關數組,該片段可以是整個數組,也可以是數組中的某一段),因此切片是一個引用類型。切片底層實現是數組,它與數組的關係如下圖:
多個slice可以指向同一個底層相關數組,此時其中一個值改變會影響全部的值
- 切片是一個可變長的數組,它的長度可以動態修改,可以用
len()
來獲取切片的長度。切片還有另外一個屬性容量,表示切片可以達到的最大長度,可以用cap()
來獲取。切片的容量等於切片的長度+相關數組中在切片之後剩下的的長度。所以切片的長度小於等於切片的容量。 - 切片的容量是預先分配的,如果在運行的過程中,切片的長度超過了原來分配的容量,則會重新分配一個空間更大的數組,然後將值都拷貝過去
- 切片的聲明格式:
var 切片名 []元素類型
,這裏不需要指定長度,如果是數組則必須指定長度或者用...
替代 - 切片可以通過底層相關數組切取,也可以用
make
直接創建,還可以通過已有的切片來生成- 通過數組生成
arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} s1 := arr[2:5] //切取數組arr索引從2~4這一段(即[3,4,5]),注意索引值不包括5 s2 := arr[:] //切片爲整個arr數組 s3 := arr[5:] //切取從索引5開始到末尾,即[6,7,8,9,10]
- 通過
make
創建
格式:make([]type, len, cap)
,其中cap
可以省略,省略則默認和len
相同s4 := make([]int, 3, 20) s5 := make([]int, 3)
- 通過切片生成,這種方式有時也被稱之未稱未切片重組reslice
需要注意的是,新的切片的索引不能超過原切片的容量,否則會引發編譯錯誤,而不是重新分配數組sa := []int{1, 2, 3, 4, 5} sb := sa[1:4] //[2,3,4],len:3,cap:4 sc := sb[1:4] //[3,4,5],len:3,cap:3
- 通過數組生成
- 在切片上追加元素(append)
如果要在切片上添加新的元素,可以使用append
函數,用法爲append(被追加元素的slice, 追加的元素...)
注意,如果追加後的長度未超過原slice的容量,則返回原始的slice,如果超過了,則重新分配空間更大的數組並拷貝原始數據s1 := make([]int, 3, 6) s1 = append(s1, 1, 2, 3) //[0,0,0,1,2,3],還是返回原來的slice (沒有超過原來cap) s1 = append(s1, 1, 2, 3) //[0,0,0,1,2,3,1,2,3],返回的是一個新的slice(已經超過了cap,重新分配底層數組)
- 拷貝slice
可以使用copy
函數將一個切片拷貝到另外一個切片中,用法copy(目標slice, 被拷貝的slice)
sc1 := []int{1, 2, 3, 4, 5} sc2 := []int{6, 7, 8} copy(sc1, sc2) fmt.Println(sc1) //[6 7 8 4 5] copy(sc2,sc1) fmt.Println(sc2) //[6,7,8] copy(sc2[0:2], sc1[3:5]) //指定具體位置 fmt.Println(sc2) //[4 5 8]
Map
Map是Go裏面的鍵值對集合,由key-value對組成,給定key,可以快速定位到對應的value。也被稱爲字典、哈希表等
- map中的key可以是任意能夠用
==
或者!=
操作符比較的類型,比如string
,int
、float
等,不能是函數、map、切片;value可以是任意類型 - map的聲明方式
var map變量名 map[key類型]vlaue類型
特別注意,map必須要初始化才能使用,即如果用上面這種方式,必須要有初始化的語句var m map[int]string //聲明 m = map[int]string{1:"a", 2:"b"} //初始化
map1 = map[keyType]ValueType{}
,否則將報錯。當然如果覺得太繁雜,可以使用下面的make
語句來替代,更加簡潔(主要是因爲使用前需要先分配好內存空間給map,使用初始化語句或者make
語句才能實現空間的分配) - map也屬於引用類型,聲明的時候不需要知道長度,可以動態增加,可以使用
make
來創建var m1 map[int]string = make(map[int]string) m2 := make(map[int]string) //簡要寫法 m1[1] = "ok" //插入 (1 : ok)的key-value m2[1] = "good"
make
創建還可以指定容量,make(map[key類型]value類型,cap)
,cap
爲容量,可以省略。超出容量會自動擴容,但爲了性能還是儘量提供一個大概的初始值。 - 獲取指定key的value
a:=m[1]
(獲取key爲1的value) - 插入一個k-v對(或對指定key進行修改)
m[1]="ok"
- 判斷鍵值對是否存在
上面獲取指定key的值map[key]
還有另外一種用法:value,isPresent = map[key]
,即返回兩個值,第二個值isPresent
是布爾類型,如果該key存在,該值爲true
,且value
爲該key對應的值;如果key不存在,則isPresent
爲false
,且value
爲空值。if value, ok := map1[key1];ok{ fmt.Println(value) }
- 刪除指定key的鍵值對
delete(map1, key1)
- 使用
for-range
對map進行遍歷
如果指向獲取key或者value,可以這麼使用map1 := make(map[int]string) map1[1] = "a" map1[2] = "b" map1[3] = "c" for key, value := range map1 { fmt.Println(key,value) }
需要注意的是,for key := range map1 { fmt.Println(key) } for _, vlaue := range map1 { fmt.Println(value) }
for
中獲得的key和value值都是副本,直接對這兩個值進行修改並不會對原來的map有影響。需要用map[key]
才能真正改變map中的值 - map類型的切片:注意需要使用兩次
make
,第一次分配切片,第二次分配切片中每個map元素sm := make([]map[int]string, 5) for i := range sm { sm[i] = make(map[int]string,1) sm[i][1]="ok" }
- 嵌套map的使用:即map中value也是map類型,這種情況同樣需要注意每一個嵌套的map也需要進行
make
之後才能使用(初始化分配空間)var m2 map[int]map[int]string m2 = make(map[int]map[int]string) m2[1] = make(map[int]string) //value中嵌套的map也需要進行初始化 m2[1][1] = "ok1"