數(shù)組的定義
var 數(shù)組名 [ 數(shù)組大小]數(shù)據(jù)類型
var a [5]int
數(shù)組必須定義長(zhǎng)度,且一個(gè)數(shù)組不能動(dòng)態(tài)改變長(zhǎng)度。不要擔(dān)心這個(gè)限制,切片(slice)可以彌補(bǔ)這個(gè)不足
四種初始化數(shù)組的方式
var numArr01 [3]int =[3]int{1, 2, 3}
var numArr02 = [3]int{5, 6, 7}
//這里的 [...]是規(guī)定的寫法
var numArr03 = [...]int{8, 9 ,10}
var numArr04 = [...]int{1: 800, 0: 900, 2:999}
直接賦值: var 數(shù)組名 [大小] [類型]=[大小] [類型]{{初值汗洒。父款。瞻坝。}{初值。炕吸。勉痴。瀑罗。}}
arr := [ ] [ ] int {{123}{356}}
數(shù)組是值類型
在 Go 中數(shù)組是值類型而不是引用類型乡话。這意味著當(dāng)數(shù)組變量被賦值時(shí)闸婴,將會(huì)獲得原數(shù)組(:也就是等號(hào)右面的數(shù)組)的拷貝榜配。新數(shù)組中元素的改變不會(huì)影響原數(shù)組中元素的值弃理。
值類型就是現(xiàn)金,要用直接用辆苔;引用類型是存折算灸,要用還得先去銀行取現(xiàn)。
值類型與引用類型的區(qū)別在于值類型的變量直接包含其數(shù)據(jù)驻啤,而引用類型的變量則存儲(chǔ)對(duì)象引用菲驴。
對(duì)于值類型,每個(gè)變量都有自己的數(shù)據(jù)副本骑冗,對(duì)一個(gè)變量的操作不可能影響另一個(gè)變量赊瞬。
對(duì)于引用類型,兩個(gè)變量可能引用同一個(gè)對(duì)象贼涩,因此對(duì)一個(gè)變量的操作可能影響另一個(gè)變量所引用的對(duì)象巧涧。
值類型 : 基本數(shù)據(jù)類型int、float遥倦、bool谤绳、string以及數(shù)組和struct。
引用類型:指針袒哥、slice缩筛、map、chan等都是引用類型统诺。
值類型:變量直接存儲(chǔ),內(nèi)存通常在棧中分配疑俭。
引用類型:變量存儲(chǔ)的是一個(gè)地址粮呢,這個(gè)地址存儲(chǔ)最終的值。內(nèi)存通常在堆上分配钞艇。通過(guò)GC回收啄寡。
通常在函數(shù)中轉(zhuǎn)入指針效率比較高,因?yàn)榉椒ㄖ械膮?shù)是需要進(jìn)行拷貝的哩照,拷貝指針的效率比較高挺物,要是一個(gè)大的接口體的話拷貝的效率就比較低。
同樣的飘弧,如果將數(shù)組作為參數(shù)傳遞給函數(shù)识藤,仍然是值傳遞砚著,在函數(shù)中對(duì)(作為參數(shù)傳入的)數(shù)組的修改不會(huì)造成原數(shù)組的改變。
數(shù)組的長(zhǎng)度
內(nèi)置函數(shù) len 用于獲取數(shù)組的長(zhǎng)度:len(a)
數(shù)組的遍歷
1.普通方式遍歷
func main() {
a := [...]float64{67.7, 89.8, 21, 78}
for i := 0; i < len(a); i++ {
fmt.Printf("%d th element of a is %.2f\n", i, a[i])
}
}
2.for-range 結(jié)構(gòu)遍歷
func main() {
a := [...]float64{67.7, 89.8, 21, 78}
for i, v := range a {
fmt.Printf("%d the element of a is %.2f\n", i, v)
}
}
range 返回?cái)?shù)組的索引和索引對(duì)應(yīng)的值
多維數(shù)組
func printarray(a [3][2]string) {
for _, v1 := range a {
for _, v2 := range v1 {
fmt.Printf("%s ", v2)
}
fmt.Printf("\n")
}
}
func main() {
a := [3][2]string{
{"lion", "tiger"},
{"cat", "dog"},
{"pigeon", "peacock"}, //this comma is necessary. The compiler will complain if you omit this comma
}
printarray(a)
var b [3][2]string
b[0][0] = "apple"
b[0][1] = "samsung"
b[1][0] = "microsoft"
b[1][1] = "google"
b[2][0] = "AT&T"
b[2][1] = "T-Mobile"
fmt.Printf("\n")
printarray(b)
}
上利用速記聲明創(chuàng)建了一個(gè)二維數(shù)組 a
痴昧,逗號(hào)是必須的稽穆,這是因?yàn)樵~法分析器會(huì)根據(jù)一些簡(jiǎn)單的規(guī)則自動(dòng)插入分號(hào)。
在第 23 行聲明了另一個(gè)二維數(shù)組 b
赶撰,并通過(guò)索引的方式給數(shù)組 b
中的每一個(gè)元素賦值舌镶。這是初始化二維數(shù)組的另一種方式。聲明的函數(shù) printarray
通過(guò)兩個(gè)嵌套的 range for 打印二維數(shù)組的內(nèi)容豪娜。
遍歷一次的話就是遍歷出里面的數(shù)組餐胀,要是想遍歷出所有的元素要進(jìn)行嵌套循環(huán)
輸出為:
lion tiger
cat dog
pigeon peacock
apple samsung
microsoft google
AT&T T-Mobile
基本語(yǔ)法
for index ,value := range arry01{
}
index 返回的是數(shù)組的下標(biāo)瘤载,value是該下標(biāo)對(duì)應(yīng)的值否灾。
-
二維數(shù)組的遍歷
-
for ---- range
for i , v := range arr{----------- v 是一個(gè)一維數(shù)組
for j , v1 := range v{
..............................................
}
}
-
雙重for循環(huán):
for i:=0;i<len(arr);i++{
for j:=0;j<len (arr[ i ]);j++{
................................................
}
}
-
切片
數(shù)組的長(zhǎng)度是固定的,沒(méi)辦法動(dòng)態(tài)增加數(shù)組的長(zhǎng)度惕虑。而切片卻沒(méi)有這個(gè)限制坟冲,切片更靈活,實(shí)際上在 Go 中溃蔫,切片比數(shù)組更為常見健提。切片并不存儲(chǔ)任何元素而只是對(duì)現(xiàn)有數(shù)組的引用。切片[ ]中沒(méi)數(shù)字伟叛,數(shù)組中[ ]內(nèi)有數(shù)字私痹。
方式一
func main() {
a := [5]int{76, 77, 78, 79, 80}
var b []int = a[1:4] //creates a slice from a[1] to a[3]
fmt.Println(b)
}
通過(guò) a[start:end]
這樣的語(yǔ)法創(chuàng)建了一個(gè)從 a[start]
到 a[end -1]
的切片。在上面的程序中统刮,第 9 行 a[1:4]
創(chuàng)建了一個(gè)從 a[1]
到 a[3]
的切片紊遵。因此 b
的值為:[77 78 79]
。
方式二
func main() {
c := []int{6, 7, 8} //creates and array and returns a slice reference
fmt.Println(c)
}
在上面的程序中侥蒙,第 9 行 c := []int{6, 7, 8}
創(chuàng)建了一個(gè)長(zhǎng)度為 3 的 int 數(shù)組暗膜,并返回一個(gè)切片給 c。
修改切片
切片本身不包含任何數(shù)據(jù)鞭衩。它僅僅是底層數(shù)組的一個(gè)上層表示学搜。對(duì)切片進(jìn)行的任何修改都將反映在底層數(shù)組中。
func main() {
darr := [...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}
dslice := darr[2:5]
fmt.Println("array before",darr)
for i := range dslice {
dslice[i]++
}
fmt.Println("array after",darr)
}
上面程序的第 9 行论衍,我們創(chuàng)建了一個(gè)從 darr[2]
到 darr[5]
的切片 dslice
瑞佩。for
循環(huán)將這些元素值加 1
。執(zhí)行完 for
語(yǔ)句之后打印原數(shù)組的值坯台,我們可以看到原數(shù)組的值被改變了炬丸。程序輸出如下:
array before [57 89 90 82 100 78 67 69 59]
array after [57 89 91 83 101 78 67 69 59]
當(dāng)若干個(gè)切片共享同一個(gè)底層數(shù)組時(shí),對(duì)每一個(gè)切片的修改都會(huì)反映在底層數(shù)組中
func main() {
numa := [3]int{78, 79 ,80}
nums1 := numa[:] //creates a slice which contains all elements of the array
nums2 := numa[:]
fmt.Println("array before change 1",numa)
nums1[0] = 100
fmt.Println("array after modification to slice nums1", numa)
nums2[1] = 101
fmt.Println("array after modification to slice nums2", numa)
}
可以看到蜒蕾,在第 9 行稠炬, numa[:]
中缺少了開始和結(jié)束的索引值焕阿,這種情況下開始和結(jié)束的索引值默認(rèn)為 0
和len(numa)
。這里 nums1
和 nums2
共享了同一個(gè)數(shù)組酸纲。程序的輸出為:
array before change 1 [78 79 80]
array after modification to slice nums1 [100 79 80]
array after modification to slice nums2 [100 101 80]
從輸出結(jié)果可以看出捣鲸,當(dāng)多個(gè)切片共享同一個(gè)數(shù)組時(shí),對(duì)每一個(gè)切片的修改都將會(huì)反映到這個(gè)數(shù)組中闽坡。
切片的長(zhǎng)度和容量
切片的長(zhǎng)度是指切片中元素的個(gè)數(shù)栽惶。切片的容量是指從切片的起始元素開始到其底層數(shù)組中的最后一個(gè)元素的個(gè)數(shù)。
func main() {
fruitarray := [...]string{"apple", "orange", "grape", "mango", "water melon", "pine apple", "chikoo"}
fruitslice := fruitarray[1:3]
fmt.Printf("length of slice %d capacity %d", len(fruitslice), cap(fruitslice)) //length of is 2 and capacity is 6
}
譯者注:使用內(nèi)置函數(shù) cap 返回切片的容量疾嗅。
func main() {
fruitarray := [...]string{"apple", "orange", "grape", "mango", "water melon", "pine apple", "chikoo"}
fruitslice := fruitarray[1:3]
fmt.Printf("length of slice %d capacity %d", len(fruitslice), cap(fruitslice)) //length of is 2 and capacity is 6
}
在上面的程序中外厂,創(chuàng)建了一個(gè)以 fruitarray
為底層數(shù)組,索引從 1
到 3
的切片 fruitslice
代承。因此 fruitslice
長(zhǎng)度為2
汁蝶。
fruitarray
的長(zhǎng)度是 7。fruiteslice
是從 fruitarray
的索引 1
開始的论悴。因此 fruiteslice
的容量是從 fruitarray
的第 1
個(gè)元素開始算起的數(shù)組中的元素個(gè)數(shù)掖棉,這個(gè)值是 6
。因此 fruitslice
的容量是 6
膀估。程序的輸出為:length of slice 2 capacity 6幔亥。
切片的長(zhǎng)度可以動(dòng)態(tài)的改變(最大為其容量)。任何超出最大容量的操作都會(huì)發(fā)生運(yùn)行時(shí)錯(cuò)誤察纯。
用make創(chuàng)建切片
內(nèi)置函數(shù) func make([]T, len, cap) []T 可以用來(lái)創(chuàng)建切片帕棉,該函數(shù)接受長(zhǎng)度和容量作為參數(shù),返回切片饼记。容量是可選的香伴,默認(rèn)與長(zhǎng)度相同。使用 make 函數(shù)將會(huì)創(chuàng)建一個(gè)數(shù)組并返回它的切片具则。
func main() {
i := make([]int, 5, 5)
fmt.Println(i)
}
用 make
創(chuàng)建的切片的元素值默認(rèn)為 0 值即纲。上面的程序輸出為:[0 0 0 0 0]
。
追加元素到切片
我們已經(jīng)知道數(shù)組是固定長(zhǎng)度的博肋,它們的長(zhǎng)度不能動(dòng)態(tài)增加低斋。而切片是動(dòng)態(tài)的,可以使用內(nèi)置函數(shù) append
添加元素到切片束昵。append
的函數(shù)原型為:append(s []T, x ...T) []T
拔稳。
x …T 表示 append
函數(shù)可以接受的參數(shù)個(gè)數(shù)是可變的葛峻。這種函數(shù)叫做變參函數(shù)锹雏。
你可能會(huì)問(wèn)一個(gè)問(wèn)題:如果切片是建立在數(shù)組之上的,而數(shù)組本身不能改變長(zhǎng)度术奖,那么切片是如何動(dòng)態(tài)改變長(zhǎng)度的呢礁遵?實(shí)際發(fā)生的情況是轻绞,當(dāng)新元素通過(guò)調(diào)用 append
函數(shù)追加到切片末尾時(shí),如果超出了容量佣耐,append
內(nèi)部會(huì)創(chuàng)建一個(gè)新的數(shù)組政勃。并將原有數(shù)組的元素被拷貝給這個(gè)新的數(shù)組,最后返回建立在這個(gè)新數(shù)組上的切片兼砖。這個(gè)新切片的容量是舊切片的二倍(譯者注:當(dāng)超出切片的容量時(shí)奸远,append
將會(huì)在其內(nèi)部創(chuàng)建新的數(shù)組,該數(shù)組的大小是原切片容量的 2 倍讽挟。最后 append
返回這個(gè)數(shù)組的全切片懒叛,即從 0 到 length - 1 的切片)。下面的程序使事情變得明朗:
func main() {
cars := []string{"Ferrari", "Honda", "Ford"}
fmt.Println("cars:", cars, "has old length", len(cars), "and capacity", cap(cars)) //capacity of cars is 3
cars = append(cars, "Toyota")
fmt.Println("cars:", cars, "has new length", len(cars), "and capacity", cap(cars)) //capacity of cars is doubled to 6
}
在上面的程序中耽梅,cars
的容量開始時(shí)為 3薛窥。在第 10 行我們追加了一個(gè)新的元素給 cars
,并將 append(cars, "Toyota")
的返回值重新復(fù)制給 cars
⊙劢悖現(xiàn)在 cars
的容量翻倍诅迷,變?yōu)?6。上面的程序輸出為:
cars: [Ferrari Honda Ford] has old length 3 and capacity 3
cars: [Ferrari Honda Ford Toyota] has new length 4 and capacity 6
切片的 0 值為 nil众旗。一個(gè) nil 切片的長(zhǎng)度和容量都為 0罢杉。可以利用 append 函數(shù)給一個(gè) nil 切片追加值逝钥。
func main() {
var names []string //zero value of a slice is nil
if names == nil {
fmt.Println("slice is nil going to append")
names = append(names, "John", "Sebastian", "Vinay")
fmt.Println("names contents:",names)
}
}
在上面的程序中 names
為 nil
屑那,并且我們把 3 個(gè)字符串追加給 names
。程序的輸出為:
slice is nil going to append
names contents: [John Sebastian Vinay]
可以使用 ... 操作符將一個(gè)切片追加到另一個(gè)切片末尾:
func main() {
veggies := []string{"potatoes","tomatoes","brinjal"}
fruits := []string{"oranges","apples"}
food := append(veggies, fruits...)
fmt.Println("food:",food)
}
上面的程序中艘款,在第10行將 fruits
追加到 veggies
并賦值給 food
持际。...
操作符用來(lái)展開切片。程序的輸出為:food: [potatoes tomatoes brinjal oranges apples]
哗咆。
切片作為函數(shù)參數(shù)
可以認(rèn)為切片在內(nèi)部表示為如下的結(jié)構(gòu)體:
type slice struct {
Length int
Capacity int
ZerothElement *byte
}
可以看到切片包含長(zhǎng)度蜘欲、容量、以及一個(gè)指向首元素的指針晌柬。當(dāng)將一個(gè)切片作為參數(shù)傳遞給一個(gè)函數(shù)時(shí)姥份,雖然是值傳遞,但是指針始終指向同一個(gè)數(shù)組年碘。因此將切片作為參數(shù)傳給函數(shù)時(shí)澈歉,函數(shù)對(duì)該切片的修改在函數(shù)外部也可以看到。讓我們寫一個(gè)程序來(lái)驗(yàn)證這一點(diǎn)屿衅。
func subtactOne(numbers []int) {
for i := range numbers {
numbers[i] -= 2
}
}
func main() {
nos := []int{8, 7, 6}
fmt.Println("slice before function call", nos)
subtactOne(nos) //function modifies the slice
fmt.Println("slice after function call", nos) //modifications are visible outside
}
在上面的程序中埃难,第 17 行將切片中的每個(gè)元素的值減2
。在函數(shù)調(diào)用之后打印切片的的內(nèi)容,發(fā)現(xiàn)切片內(nèi)容發(fā)生了改變涡尘。你可以回想一下忍弛,這不同于一個(gè)數(shù)組,對(duì)函數(shù)內(nèi)部的數(shù)組所做的更改在函數(shù)外不可見考抄。上面的程序輸出如下:
array before function call [8 7 6]
array after function call [6 5 4]
多維切片
同數(shù)組一樣细疚,切片也可以有多個(gè)維度。
func main() {
pls := [][]string {
{"C", "C++"},
{"JavaScript"},
{"Go", "Rust"},
}
for _, v1 := range pls {
for _, v2 := range v1 {
fmt.Printf("%s ", v2)
}
fmt.Printf("\n")
}
}
上面程序的輸出如下:
C C++
JavaScript
Go Rust
內(nèi)存優(yōu)化
切片保留對(duì)底層數(shù)組的引用川梅。只要切片存在于內(nèi)存中疯兼,數(shù)組就不能被垃圾回收。這在內(nèi)存管理方便可能是值得關(guān)注的贫途。假設(shè)我們有一個(gè)非常大的數(shù)組镇防,而我們只需要處理它的一小部分,為此我們創(chuàng)建這個(gè)數(shù)組的一個(gè)切片潮饱,并處理這個(gè)切片来氧。這里要注意的事情是,數(shù)組仍然存在于內(nèi)存中香拉,因?yàn)榍衅谝盟?/p>
解決該問(wèn)題的一個(gè)方法是使用 copy 函數(shù) func copy(dst, src []T) int
來(lái)創(chuàng)建該切片的一個(gè)拷貝啦扬。這樣我們就可以使用這個(gè)新的切片,原來(lái)的數(shù)組可以被垃圾回收凫碌。
func countries() []string {
countries := []string{"USA", "Singapore", "Germany", "India", "Australia"}
neededCountries := countries[:len(countries)-2]
countriesCpy := make([]string, len(neededCountries))
copy(countriesCpy, neededCountries) //copies neededCountries to countriesCpy
return countriesCpy
}
func main() {
countriesNeeded := countries()
fmt.Println(countriesNeeded)
}
在上面程序中扑毡,第 9 行 neededCountries := countries[:len(countries)-2]
創(chuàng)建一個(gè)底層數(shù)組為 countries
并排除最后兩個(gè)元素的切片。第 11 行將 neededCountries
拷貝到 countriesCpy
并在下一行返回 countriesCpy
∈⑾眨現(xiàn)在數(shù)組countries
可以被垃圾回收瞄摊,因?yàn)?neededCountries
不再被引用。
系列文章來(lái)源于網(wǎng)絡(luò)和本人總結(jié)苦掘,只作學(xué)習(xí)備忘使用换帜。
本文參考:https://blog.csdn.net/u011304970/article/details/74938457