數(shù)組數(shù)組是同一種數(shù)據(jù)類型元素的集合。在Go語言中,數(shù)組從聲明時就確定,可以對其成員進行修改,但是不可以修改數(shù)組的大小。 數(shù)組的定義數(shù)組的基本定義語法如下: var 數(shù)組名 [數(shù)組大小]類型
比如定義一個長度為3,類型是int的數(shù)組: var a [3]int
注意:長度必須是常量,它是數(shù)組類型的一部分,一旦定義,長度不能改變。 數(shù)組的初始化(1)、初始化數(shù)組時可以使用初始化列表來設(shè)置數(shù)組元素的值。 func main() {
var testArray [3]int //數(shù)組會初始化為int類型的零值
var numArray = [3]int{1, 2} //使用指定的初始值完成初始化
var cityArray = [3]string{"北京", "上海", "深圳"} //使用指定的初始值完成初始化
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2 0]
fmt.Println(cityArray) //[北京 上海 深圳]
}
(2)、按照上面的方法每次都要確保提供的初始值和數(shù)組長度一致,一般情況下我們可以讓編譯器根據(jù)初始值的個數(shù)自行推斷數(shù)組的長度,例如: func main() {
var testArray [3]int
var numArray = [...]int{1, 2}
var cityArray = [...]string{"北京", "上海", "深圳"}
fmt.Println(testArray) //[0 0 0]
fmt.Println(numArray) //[1 2]
fmt.Printf("type of numArray:%T\n", numArray) //type of numArray:[2]int
fmt.Println(cityArray) //[北京 上海 深圳]
fmt.Printf("type of cityArray:%T\n", cityArray) //type of cityArray:[3]string
}
(3)、我們還可以使用指定索引值的方式來初始化數(shù)組,例如: func main() {
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]
fmt.Printf("type of a:%T\n", a) //type of a:[4]int
}
數(shù)組的遍歷數(shù)組的遍歷有兩種方法: 如下: package main
import "fmt"
func main() {
var a = [...]string{"北京", "上海", "廣州"}
// 通過索引遍歷數(shù)組
for i := 0; i < len(a); i {
fmt.Println(a[i])
}
fmt.Println("================")
// 用for range遍歷數(shù)組
for _, v := range a {
fmt.Println(v)
}
}
多維數(shù)組Go語言支持多維數(shù)組,這里以二維數(shù)組為例。 二維數(shù)組的定義二維數(shù)組的基本定義如下: var 數(shù)組名 [數(shù)組大小][數(shù)組大小]類型
如下表示外層數(shù)組有三個元素,里層數(shù)組有兩個元素的二維數(shù)組: var a [3][2]int
二維數(shù)組的初始化package main
import "fmt"
func main() {
var cities = [3][2]string{
{"北京", "上海"},
{"廣州", "重慶"},
{"四川", "貴州"},
}
fmt.Println(cities)
}
如果要用自動推導(dǎo)數(shù)組長度,只有第一層可以使用,如下: //支持的寫法
a := [...][2]string{
{"北京", "上海"},
{"廣州", "深圳"},
{"成都", "重慶"},
}
//不支持多維數(shù)組的內(nèi)層使用...
b := [3][...]string{
{"北京", "上海"},
{"廣州", "深圳"},
{"成都", "重慶"},
}
注意:數(shù)組是值類型的,其賦值和傳參會復(fù)制整個數(shù)組,因此改變副本的值,不會改變本身的值。如下: package main
import "fmt"
func main() {
var cities = [3][2]string{
{"北京", "上海"},
{"廣州", "重慶"},
{"四川", "貴州"},
}
// fmt.Println(cities)
c2 := cities
c2[2][0] = "黑龍江"
fmt.Println(cities)
fmt.Println(c2)
}
其輸出結(jié)果為: [[北京 上海] [廣州 重慶] [四川 貴州]]
[[北京 上海] [廣州 重慶] [黑龍江 貴州]]
3.3、切片數(shù)組的長度是不可變的,一旦我們定義了某個數(shù)組,其長度就固定了。而切片是對數(shù)組的一層封裝,它是擁有相同類型元素的可變長度序列。它非常靈活,支持自動擴容。 切片是一個引用類型,其底層依然是某個數(shù)組,如果底層數(shù)據(jù)改變,切片也會相應(yīng)的改變。切片的內(nèi)容包括地址 長度 容量 。它一般用來快速的操作一塊數(shù)據(jù)集合。 自定義切片切片的基本語法如下: var 變量名 []元素類型
注意:在定義切片的時候,不指定其長度。
例如: package main
import "fmt"
func main() {
// 定義切片
var s1 []int
var s2 []string
var s3 []bool
fmt.Println(s1, s2, s3)
}
由于切片是引用類型,它不支持直接比較,只支持和nil 比較,如下: package main
import "fmt"
func main() {
// 定義切片
var s1 []int
var s2 []string
var s3 []bool
fmt.Println(s1, s2, s3)
fmt.Println(s1 == nil)
fmt.Println(s2 == nil)
fmt.Println(s3 == nil)
// fmt.Println(s1 == s3) // 不能這么比較
}
自定義切片的初始化切片的初始化和數(shù)組一樣,因為其底層本身就是數(shù)組。比如: package main
import "fmt"
func main() {
// 定義切片并初始化
var s4 = []int{1, 2, 3, 4}
var s5 = []string{"北京", "上海"}
var s6 = []bool{true, false}
fmt.Println(s4, s5, s6)
}
基于數(shù)組定義切片我們知道切片底層是存儲,所以切片也可以基于數(shù)組進行定義。如下: package main
import "fmt"
func main() {
// 基于數(shù)組定義切片
// 先定義一個數(shù)組
var a1 = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// 然后對數(shù)組進行切割
var a2 = a1[2:6]
var a3 = a1[1:]
var a4 = a1[:4]
var a5 = a1[:]
fmt.Println(a1, a2)
}
基于切片定義切片切片還可以基于切片定義切片。如下: package main
import "fmt"
func main() {
// 基于切片定義切片
// 定義一個切片
var s = []int{1,2,3,4,5,6,7,8,9}
// 對切片進行切割
var s1 = s[2:4]
fmt.Println(s1)
}
注意:對切片進行再切片的時候,其索引不能超過原數(shù)組的長度。
切片的長度和容量切片擁有自己的長度和容量,我們可以通過使用內(nèi)置的len() 函數(shù)求長度,使用內(nèi)置的cap() 函數(shù)求切片的容量。 package main
import "fmt"
func main() {
// 基于切片定義切片
// 定義一個切片
var s = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// 對切片進行切割
var s1 = s[2:4]
fmt.Println(s1)
fmt.Printf("len(s1):%d cap(s1):%d", len(s1), cap(s1))
}
輸出如下: [3 4]
len(s1):2 cap(s1):7
說明: 用make函數(shù)構(gòu)造切片上面介紹的都是通過數(shù)組來定義切片,不論是通過自定義還是從數(shù)組切片來的,其長度和容量都在定義的時候固定了。那么,如果我們需要動態(tài)創(chuàng)建一個切片,就需要使用make 函數(shù),格式如下: make([]T, size, cap)
說明: T:表示元素的類型 size:表示元素的數(shù)量 cap:表示切片的容量
比如,我們創(chuàng)建一個元素個數(shù)是5,容量是10的切片。 package main
import "fmt"
func main() {
s := make([]int, 5, 10)
fmt.Println(s)
fmt.Printf("len(s):%d cap(s):%d\n", len(s), cap(s))
}
輸出如下: [0 0 0 0 0]
len(s):5 cap(s):10
如果我們不指定容量,其默認(rèn)和前面的元素個數(shù)一致,如下: package main
import "fmt"
func main() {
s := make([]int, 5)
fmt.Println(s)
fmt.Printf("len(s):%d cap(s):%d", len(s), cap(s))
}
輸出如下: [0 0 0 0 0]
len(s):5 cap(s):5
切片的本質(zhì)切片的本質(zhì)就是對底層數(shù)組的封裝,它包含了三個信息:底層數(shù)組的指針、切片的長度(len)和切片的容量(cap)。 舉個例子,現(xiàn)在有一個數(shù)組a := [8]int{0, 1, 2, 3, 4, 5, 6, 7} ,切片s1 := a[:5] ,相應(yīng)示意圖如下。 
切片s2 := a[3:6] ,相應(yīng)示意圖如下: 
切片之間是不能比較的,我們不能使用== 操作符來判斷兩個切片是否含有全部相等元素。 切片唯一合法的比較操作是和nil 比較。 一個nil 值的切片并沒有底層數(shù)組,一個nil 值的切片的長度和容量都是0。但是我們不能說一個長度和容量都是0的切片一定是nil ,例如下面的示例: var s1 []int //len(s1)=0;cap(s1)=0;s1==nil
s2 := []int{} //len(s2)=0;cap(s2)=0;s2!=nil
s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
所以要判斷一個切片是否是空的,要是用len(s) == 0 來判斷,不應(yīng)該使用s == nil 來判斷。 注意以下三點:
切片的賦值拷貝我們知道切片的本質(zhì)是對底層數(shù)組的封裝,是一個引用類型,所以如果兩個切片共用底層數(shù)組,那么對一個切片進行修改會影響另一個切片的內(nèi)容,如下: package main
import "fmt"
func main() {
s := make([]int, 5)
fmt.Println(s)
fmt.Printf("len(s):%d cap(s):%d\n", len(s), cap(s))
s1 := s
s1[0] = 100
fmt.Println(s)
fmt.Println(s1)
}
其輸出結(jié)果如下: [0 0 0 0 0]
len(s):5 cap(s):5
[100 0 0 0 0]
[100 0 0 0 0]
用copy函數(shù)復(fù)制切片上面介紹的賦值拷貝,兩個切片是共用同一個底層數(shù)組,對任意一個進行修改就會影響另一個切片。Go語言中有另一個函數(shù)copy() ,它是會開辟另外一個內(nèi)存空間用來保存復(fù)制來的切片,通過這個函數(shù)復(fù)制的切片與原切片沒有任何關(guān)系。 其基本語法如下: copy(destSlice, srcSlice)
其中: destSlice:是目標(biāo)切片 srcSlice:是源切片
例如: package main
import "fmt"
func main() {
// 定義一個切片并初始化
s1 := []int{1, 2, 3, 4}
// 再定義一個切片
s2 := make([]int, 4, 4)
// 用copy()函數(shù)進行復(fù)制
copy(s2, s1)
// 輸出:源切片s1:[1 2 3 4] 目標(biāo)切片s2:[1 2 3 4]
fmt.Printf("源切片s1:%v 目標(biāo)切片s2:%v\n", s1, s2)
// 對源切片進行修改,觀察目標(biāo)切片有無變化
s1[0] = 100
// 輸出:源切片s1:[100 2 3 4] 目標(biāo)切片s2:[1 2 3 4]
fmt.Printf("源切片s1:%v 目標(biāo)切片s2:%v\n", s1, s2)
// 對目標(biāo)切片進行修改,觀察源切片有無變化
s2[1] = 300
// 輸出:源切片s1:[100 2 3 4] 目標(biāo)切片s2:[1 300 3 4]
fmt.Printf("源切片s1:%v 目標(biāo)切片s2:%v\n", s1, s2)
}
從上面可以看出,用copy 復(fù)制的切片,相互之前并不影響。 切片的遍歷切片的遍歷和數(shù)組一樣,如下: package main
import "fmt"
func main() {
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// 第一種:遍歷索引
for i := 0; i < len(s); i {
fmt.Println(s[i])
}
// 第二種:用for range遍歷
for i, v := range s {
fmt.Println(i, v)
}
}
給切片添加元素上面介紹了切片的長度不是固定,那么我們就可以往切片里添加新的內(nèi)容。在Go語言中,給切片添加內(nèi)容是用append() 方法。 注意: 如果切片的容量夠用,則直接向切片中添加內(nèi)容; 如果切片的容量不夠,則會先一定的策略進行擴容,然后再向里面添加內(nèi)容;
比如: package main
import "fmt"
func main() {
// 定義一個數(shù)組
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// 然后從數(shù)組得到一個切片
s := a[:4]
// 然后打印切片的長度和容量,這時候的長度是小于容量的。
// 輸出:[1 2 3 4] len(s):4 cap(s):9
fmt.Printf("%v len(s):%d cap(s):%d\n", s, len(s), cap(s))
// 我們對切片s進行添加元素操作
s = append(s, 100)
// 輸出:[1 2 3 4 100] len(s):5 cap(s):9
fmt.Printf("%v len(s):%d cap(s):%d\n", s, len(s), cap(s))
// 我們重新從數(shù)組得到一個切片,這次用全量的
s2 := a[:]
// 輸出:[1 2 3 4 100 6 7 8 9] len(s2):9 cap(s2):9
fmt.Printf("%v len(s2):%d cap(s2):%d\n", s2, len(s2), cap(s2))
// 我們對其進行添加元素
s2 = append(s2, 200)
// 輸出:[1 2 3 4 100 6 7 8 9 200] len(s2):10 cap(s2):18
fmt.Printf("%v len(s2):%d cap(s2):%d\n", s2, len(s2), cap(s2))
}
上面的輸出正好驗證了我們所說,如果切片的容量充足,添加元素則直接再后面追加;如果切片容量不足,添加元素則先對容量進行擴充,再在后面添加元素。 當(dāng)然,append() 函數(shù)還支持一次性擴容多個元素,如下: package main
import "fmt"
func main() {
// 定義一個數(shù)組
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
// 我們重新從數(shù)組得到一個切片,這次用全量的
s2 := a[:]
// 輸出:[1 2 3 4 100 6 7 8 9] len(s2):9 cap(s2):9
fmt.Printf("%v len(s2):%d cap(s2):%d\n", s2, len(s2), cap(s2))
// 我們對其進行添加元素
s2 = append(s2, 200)
// 輸出:[1 2 3 4 100 6 7 8 9 200] len(s2):10 cap(s2):18
fmt.Printf("%v len(s2):%d cap(s2):%d\n", s2, len(s2), cap(s2))
s2 = append(s2, 300, 400, 500)
fmt.Printf("%v len(s2):%d cap(s2):%d\n", s2, len(s2), cap(s2))
}
切片的擴容策略通過上面對切片添加元素知道當(dāng)切片的容量不足的時候會先給切片擴容。擴容也是有一定策略的,其策略如下: 如果申請的容量大于原來容量的2倍,最終的容量則是申請的容量大?。?/p> 如果舊切片的容量小于1024,則最終容量是舊容量的2倍; 如果舊切片的容量大于1024,則最終容量是從舊容量開始循環(huán)增加原來的1/4,直到最終容量大于等于新申請的容量; 如果最終容量計算值溢出,則最終容量是新申請容量;
注意:切片擴容還會根據(jù)切片中元素的類型不同而做不同的處理,比如int 和string 類型的處理方式就不一樣。
從切片中刪除元素Go語言中并沒有刪除切片元素的專用方法,但是我們可以使用切片本身的特性來刪除元素。 比如要刪除切片a中所以為index 的元素,則用以下方法: a = append(a[:index], a[index 1:]...)
例如: package main
import "fmt"
func main() {
// 定義一個切片
s1 := []int{1, 2, 3, 4, 5}
// 從切片中刪除值為3的元素
s1 = append(s1[:2], s1[3:]...)
// 輸出:[1 2 4 5]
fmt.Println(s1)
}
我們從切片中刪除某個元素對原數(shù)組有什么影響呢?我們看下面這個例子: package main
import "fmt"
func main() {
// 定義一個數(shù)組
a := [...]int{1, 2, 3, 4, 5, 6}
s2 := a[:]
s2 = append(s2[:2], s2[3:]...)
fmt.Println(s2) // 輸出:[1 2 4 5 6]
fmt.Println(a) // 輸出:[1 2 4 5 6 6]
}
我們可以看到從切片中刪除某個元素,對原數(shù)據(jù)也有影響。如下圖:  來源:https://www./content-4-660551.html
|