一文教你如何快速學(xué)會(huì)Go的切片和數(shù)組數(shù)據(jù)類型
什么是數(shù)組
數(shù)組是屬于同一類型的元素的集合。例如,整數(shù) 5、8、9、79、76 的集合形成一個(gè)數(shù)組。Go 中不允許混合不同類型的值,例如,同時(shí)包含字符串和整數(shù)的數(shù)組。
聲明數(shù)組
數(shù)組屬于類型 。 表示數(shù)組中的元素?cái)?shù),并表示每個(gè)元素的類型。元素的數(shù)量也是類型的一部分(我們稍后將對(duì)此進(jìn)行更詳細(xì)的討論。[n]TnTn
有不同的方法來(lái)聲明數(shù)組。讓我們一個(gè)接一個(gè)地看一下。
package main import ( "fmt" ) func main() { var a [3]int //int array with length 3 fmt.Println(a) }
var a [3]int 聲明一個(gè)長(zhǎng)度為 3 的整數(shù)數(shù)組。數(shù)組中的所有元素都將自動(dòng)分配數(shù)組類型的零值。在這種情況下是一個(gè)整數(shù)數(shù)組,因此的所有元素都賦給 ,int 的零值。運(yùn)行上述程序?qū)⒋蛴?code>a a 0
[0 0 0]
數(shù)組的索引從 開始,到 結(jié)束于 。讓我們?yōu)樯厦娴臄?shù)組分配一些值。0 length - 1。
package main import ( "fmt" ) func main() { var a [3]int //int array with length 3 a[0] = 12 // array index starts at 0 a[1] = 78 a[2] = 50 fmt.Println(a) }
a[0] 將值賦給數(shù)組的第一個(gè)元素。該程序?qū)⒋蛴?/p>
[12 78 50]
讓我們使用短語(yǔ)法聲明創(chuàng)建相同的數(shù)組。
package main import ( "fmt" ) func main() { // short hand declaration to create array a := [3]int{12, 78, 50} fmt.Println(a) }
上面的程序?qū)⒋蛴∠嗤妮敵?
[12 78 50]
在短語(yǔ)法聲明期間,不必為數(shù)組中的所有元素賦值。
package main import ( "fmt" ) func main() { a := [3]int{12} fmt.Println(a) }
在上面的程序中,第 8 行聲明了一個(gè)長(zhǎng)度為 3 的數(shù)組,但只提供了一個(gè)值 。其余 2 個(gè)元素將自動(dòng)指定。此程序?qū)⒋蛴?code>a := [3]int{12} 12 0
[12 0 0]
您甚至可以忽略聲明中數(shù)組的長(zhǎng)度,并將其替換為,并讓編譯器為您找到長(zhǎng)度。這是在以下程序中完成的。...
package main import ( "fmt" ) func main() { // ... makes the compiler determine the length a := [...]int{12, 78, 50} fmt.Println(a) }
數(shù)組的大小是類型的一部分。因此 和 是不同的類型。因此,無(wú)法調(diào)整數(shù)組的大小。
package main func main() { a := [3]int{5, 78, 8} var b [5]int b = a //not possible since [3]int and [5]int are distinct types }
在上面程序的第 6 行中,我們嘗試將類型的變量分配給不允許的類型變量,因此編譯器將打印以下錯(cuò)誤:
./prog.go:6:7: cannot use a (type [3]int) as type [5]int in assignment
數(shù)組是值類型
Go 中的數(shù)組是值類型,而不是引用類型。這意味著,當(dāng)它們被分配給新變量時(shí),原始數(shù)組的副本將分配給新變量。如果對(duì)新變量進(jìn)行了更改,它將不會(huì)反映在原始數(shù)組中。
package main import "fmt" func main() { a := [...]string{"USA", "China", "India", "Germany", "France"} b := a // a copy of a is assigned to b b[0] = "Singapore" fmt.Println("a is ", a) fmt.Println("b is ", b) }
上述代碼將打印出如下內(nèi)容:
a is [USA China India Germany France]
b is [Singapore China India Germany France]
同樣,當(dāng)數(shù)組作為參數(shù)傳遞給函數(shù)時(shí),它們按值傳遞,原始數(shù)組保持不變。
package main import "fmt" func changeLocal(num [5]int) { num[0] = 55 fmt.Println("inside function ", num) } func main() { num := [...]int{5, 6, 7, 8, 8} fmt.Println("before passing to function ", num) changeLocal(num) //num is passed by value fmt.Println("after passing to function ", num) }
在上面的第 13 行程序中,數(shù)組實(shí)際上是按值傳遞給函數(shù)的,因此不會(huì)因?yàn)楹瘮?shù)調(diào)用而更改。該程序?qū)⒋蛴?
before passing to function [5 6 7 8 8]
inside function [55 6 7 8 8]
after passing to function [5 6 7 8 8]
數(shù)組的長(zhǎng)度
通過(guò)將數(shù)組作為參數(shù)傳遞給函數(shù)(len)來(lái)找到數(shù)組的長(zhǎng)度。
package main import "fmt" func main() { a := [...]float64{67.7, 89.8, 21, 78} fmt.Println("length of a is",len(a)) }
上述程序?qū)⒋蛴。?/p>
length of a is 4
迭代數(shù)組
使用for可用于循環(huán)訪問(wèn)數(shù)組的元素。
package main import "fmt" func main() { a := [...]float64{67.7, 89.8, 21, 78} for i := 0; i < len(a); i++ { //looping from 0 to the length of the array fmt.Printf("%d th element of a is %.2f\n", i, a[i]) } }
上面的程序使用循環(huán)來(lái)迭代數(shù)組的元素,從 index 到 。該程序有效并將打印:
0 th element of a is 67.70
1 th element of a is 89.80
2 th element of a is 21.00
3 th element of a is 78.00
Go 提供了一種更好、更簡(jiǎn)潔的方式,通過(guò)使用循環(huán)的范圍形式來(lái)迭代數(shù)組。 返回索引和該索引處的值。讓我們使用范圍重寫上面的代碼。我們還將找到數(shù)組中所有元素的總和。
package main import "fmt" func main() { a := [...]float64{67.7, 89.8, 21, 78} sum := float64(0) for i, v := range a {//range returns both the index and value fmt.Printf("%d the element of a is %.2f\n", i, v) sum += v } fmt.Println("\nsum of all elements of a",sum) }
上述程序的第 8 行是 for 循環(huán)的范圍形式。它將返回索引和該索引處的值。我們打印值,并計(jì)算數(shù)組中所有元素的總和。該程序的輸出是:
0 the element of a is 67.70
1 the element of a is 89.80
2 the element of a is 21.00
3 the element of a is 78.00
sum of all elements of a 256.5
如果您只想要該值并希望忽略索引,則可以通過(guò)將索引替換為空白標(biāo)識(shí)符來(lái)執(zhí)行此操作。_
for _, v := range a { //ignores index }
上面的 for 循環(huán)忽略了索引。同樣,該值也可以忽略。
多維數(shù)組
到目前為止,我們創(chuàng)建的數(shù)組都是單維的??梢詣?chuàng)建多維數(shù)組。
package main import ( "fmt" ) 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) }
在上面的程序的第 17 行中,使用短手語(yǔ)法聲明了一個(gè)二維字符串?dāng)?shù)組。第 20 行末尾的逗號(hào)是必需的。這是因?yàn)樵~法分析器根據(jù)簡(jiǎn)單規(guī)則自動(dòng)插入分號(hào)。另一個(gè) 2d 數(shù)組在第 23 行中聲明,并為每個(gè)索引逐個(gè)添加字符串。這是初始化 2d 數(shù)組的另一種方法。
第 7 行中的函數(shù)使用兩個(gè) for 范圍循環(huán)來(lái)打印 2d 數(shù)組的內(nèi)容。以上程序?qū)⒋蛴。?/p>
lion tiger
cat dog
pigeon peacock
apple samsung
microsoft google
AT&T T-Mobile
數(shù)組就是這樣。盡管數(shù)組似乎足夠靈活,但它們具有固定長(zhǎng)度的限制。不能增加數(shù)組的長(zhǎng)度。這就是切片進(jìn)入畫面的地方。事實(shí)上,在 Go 中,切片比傳統(tǒng)數(shù)組更常見。
切片
切片是數(shù)組頂部的方便、靈活且功能強(qiáng)大的包裝器。切片本身不擁有任何數(shù)據(jù)。它們只是對(duì)現(xiàn)有數(shù)組的引用。
創(chuàng)建切片
具有 T 類型元素的切片由下式表示[]T
。
package main import ( "fmt" ) 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) }
該語(yǔ)法從一個(gè)數(shù)組開始從一個(gè)索引到另一個(gè)索引創(chuàng)建一個(gè)切片。因此,在上面程序的第 9 行中,從索引 1 到 3 創(chuàng)建數(shù)組的切片表示形式。使用該表達(dá)式:a[start:end]
。
package main import ( "fmt" ) func main() { //creates and array and returns a slice reference c := []int{6, 7, 8} fmt.Println(c) }
在上面的函數(shù)的第 9 行中,創(chuàng)建一個(gè)包含 3 個(gè)整數(shù)的數(shù)組,并返回存儲(chǔ)在 c 中的切片引用。
修改切片
切片不擁有自己的任何數(shù)據(jù)。它只是基礎(chǔ)數(shù)組的表示形式。對(duì)切片所做的任何修改都將反映在基礎(chǔ)數(shù)組中。
package main import ( "fmt" ) 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 行中,我們從數(shù)組的索引 2、3、4 創(chuàng)建。for 循環(huán)將這些索引中的值遞增 1。當(dāng)我們?cè)趂or循環(huán)之后打印數(shù)組時(shí),我們可以看到對(duì)切片的更改反映在數(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è)基礎(chǔ)數(shù)組時(shí),每個(gè)切片所做的更改將反映在數(shù)組中。
package main import ( "fmt" ) 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 行中,缺少開始值和結(jié)束值。“開始”和“結(jié)束”的默認(rèn)值分別為 和 。兩個(gè)切片并共享同一個(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]
從輸出中可以清楚地看出,當(dāng)切片共享同一個(gè)數(shù)組時(shí)。對(duì)切片所做的修改將反映在數(shù)組中。
切片長(zhǎng)度和容量
切片的長(zhǎng)度是切片中元素的數(shù)量。切片的容量是從創(chuàng)建切片的索引開始的基礎(chǔ)數(shù)組中的元素?cái)?shù)。
package main import ( "fmt" ) 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 fruitslice is 2 and capacity is 6 }
在上面的程序中,是從 的索引 1 和 2 創(chuàng)建的。因此,的長(zhǎng)度為 2。
切片可以重新切片到其容量。超出此值的任何內(nèi)容都將導(dǎo)致程序引發(fā)運(yùn)行時(shí)錯(cuò)誤。
package main import ( "fmt" ) 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\n", len(fruitslice), cap(fruitslice)) //length of is 2 and capacity is 6 fruitslice = fruitslice[:cap(fruitslice)] //re-slicing furitslice till its capacity fmt.Println("After re-slicing length is",len(fruitslice), "and capacity is",cap(fruitslice)) }
在上述程序的第11行中,被重新切片到其容量。以上程序輸出:
length of slice 2 capacity 6
After re-slicing length is 6 and capacity is 6
使用make創(chuàng)建切片
生成切片可以使用make([]T, len, cap)來(lái)創(chuàng)建切片。[]T表示數(shù)據(jù)類型、len表示切片長(zhǎng)度、cap表示切片容量。make 函數(shù)創(chuàng)建一個(gè)數(shù)組并返回對(duì)它的切片引用。
package main import ( "fmt" ) func main() { i := make([]int, 5, 5) fmt.Println(i) }
默認(rèn)情況下,使用 make 創(chuàng)建切片時(shí),這些值將清零。上述程序?qū)⑤敵?。[0 0 0 0 0]
切片追加
正如我們已經(jīng)知道的那樣,數(shù)組被限制為固定長(zhǎng)度,并且它們的長(zhǎng)度不能增加。切片是動(dòng)態(tài)的,可以使用函數(shù)將新元素追加到切片中。追加函數(shù)的定義是 。append([]T, x...):[]T
。返回的是一個(gè)新的切片。
x ...函數(shù)定義中的 T 表示函數(shù)接受參數(shù) x 的可變數(shù)量的參數(shù)。這些類型的函數(shù)稱為可變參數(shù)函數(shù)。
不過(guò),有一個(gè)問(wèn)題可能會(huì)困擾您。如果切片由數(shù)組支持,并且數(shù)組本身具有固定長(zhǎng)度,那么切片如何具有動(dòng)態(tài)長(zhǎng)度。在引擎蓋下發(fā)生的事情是,當(dāng)新元素追加到切片時(shí),將創(chuàng)建一個(gè)新數(shù)組。現(xiàn)有數(shù)組的元素將復(fù)制到此新數(shù)組,并返回此新數(shù)組的新切片引用。新切片的容量現(xiàn)在是舊切片的兩倍。以下程序?qū)⑹故虑樽兊们逦?/p>
package main import ( "fmt" ) 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 }
在上面的程序中,容量最初為 3。我們將新元素附加到第 10 行中的汽車,并將 返回的切片再次分配給汽車?,F(xiàn)在,汽車的容量翻了一番,變成了6輛。上述程序的輸出是:
cars: [Ferrari Honda Ford] has old length 3 and capacity 3
cars: [Ferrari Honda Ford Toyota] has new length 4 and capacity 6
切片類型的零值為 。切片的長(zhǎng)度和容量為 0??梢允褂米芳雍瘮?shù)將值追加到切片。
package main import ( "fmt" ) 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) } }
在上面的程序中為 nil,我們已將 3 個(gè)字符串附加到 。程序的輸出是:
slice is nil going to append
names contents: [John Sebastian Vinay]
也可以使用運(yùn)算符將一個(gè)切片追加到另一個(gè)切片。
package main import ( "fmt" ) func main() { veggies := []string{"potatoes","tomatoes","brinjal"} fruits := []string{"oranges","apples"} food := append(veggies, fruits...) fmt.Println("food:",food) }
在上述程序的第 10 行中,通過(guò)附加到切片中。程序的輸出是:
fruitsveggiesfood: [potatoes tomatoes brinjal oranges apples]
切片作為函數(shù)參數(shù)
可以將切片視為由結(jié)構(gòu)類型在內(nèi)部表示。
type slice struct { Length int Capacity int ZerothElement *byte }
切片包含長(zhǎng)度、容量和指向數(shù)組第零個(gè)元素的指針。將切片傳遞給函數(shù)時(shí),即使它按值傳遞,指針變量也將引用相同的基礎(chǔ)數(shù)組。因此,當(dāng)切片作為參數(shù)傳遞給函數(shù)時(shí),在函數(shù)內(nèi)部所做的更改在函數(shù)外部也是可見的。讓我們編寫一個(gè)程序來(lái)檢查一下。
package main import ( "fmt" ) 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 行中的函數(shù)調(diào)用將切片的每個(gè)元素遞減 2。在函數(shù)調(diào)用后打印切片時(shí),這些更改是可見的。如果您還記得,這與數(shù)組不同,在數(shù)組中,對(duì)函數(shù)內(nèi)部的數(shù)組所做的更改在函數(shù)外部不可見。上述程序的輸出是
slice before function call [8 7 6]
slice after function call [6 5 4]
多維切片
與數(shù)組類似,切片可以具有多個(gè)維度。
package main import ( "fmt" ) func main() { pls := [][]string { {"C", "C++"}, {"JavaScript"}, {"Go", "Rust"}, } for _, v1 := range pls { for _, v2 := range v1 { fmt.Printf("%s ", v2) } fmt.Printf("\n") } }
輸出的結(jié)果是:
C C++
JavaScript
Go Rust
切片保存對(duì)基礎(chǔ)數(shù)組的引用。只要切片在內(nèi)存中,就無(wú)法對(duì)數(shù)組進(jìn)行垃圾回收。在內(nèi)存管理方面,這可能會(huì)引起關(guān)注。讓我們假設(shè)我們有一個(gè)非常大的數(shù)組,我們只對(duì)處理其中的一小部分感興趣。從此以后,我們從該數(shù)組創(chuàng)建一個(gè)切片,并開始處理該切片。這里要注意的重要一點(diǎn)是,數(shù)組仍將在內(nèi)存中,因?yàn)榍衅昧怂?/p>
解決此問(wèn)題的一種方法是使用 copy 函數(shù)來(lái)復(fù)制該切片。這樣,我們可以使用新的切片,并且可以對(duì)原始數(shù)組進(jìn)行垃圾回收。copy(dst, src []T):int
。
package main import ( "fmt" ) 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 行中,創(chuàng)建一個(gè)禁止最后 2 個(gè)元素的切片。上述程序的第 11 行復(fù)制到下一行中的函數(shù),并從中返回它?,F(xiàn)在數(shù)組可以被垃圾回收,因?yàn)樗辉俦灰谩?/p>
以上就是一文教你如何快速學(xué)會(huì)Go的切片和數(shù)組數(shù)據(jù)類型的詳細(xì)內(nèi)容,更多關(guān)于Go切片 數(shù)組的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
GO中的時(shí)間操作總結(jié)(time&dateparse)
日常開發(fā)過(guò)程中,對(duì)于時(shí)間的操作可謂是無(wú)處不在,但是想實(shí)現(xiàn)時(shí)間自由還是不簡(jiǎn)單的,多種時(shí)間格式容易混淆,本文為大家整理了一下GO中的時(shí)間操作,有需要的可以參考下2023-09-09go-zero接入skywalking實(shí)現(xiàn)鏈路追蹤的詳細(xì)教程
SkyWalking是一個(gè)開源的服務(wù)追蹤系統(tǒng),它專注于分布式系統(tǒng)的可觀測(cè)性和服務(wù)性能分析,它的主要目標(biāo)是幫助開發(fā)者理解和優(yōu)化微服務(wù)架構(gòu)中的服務(wù)間交互情況,本文介紹了go-zero接入skywalking鏈路追蹤的詳細(xì)教程,需要的朋友可以參考下2024-08-08Golang實(shí)現(xiàn)解析JSON的三種方法總結(jié)
這篇文章主要為大家詳細(xì)介紹了Golang實(shí)現(xiàn)解析JSON的三種方法,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)了解JSON有一定幫助,需要的可以參考一下2022-09-09golang實(shí)踐-第三方包為私有庫(kù)的配置方案
這篇文章主要介紹了golang實(shí)踐-第三方包為私有庫(kù)的配置方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05利用Golang解析json數(shù)據(jù)的方法示例
Go提供了原生的JSON庫(kù),并且與語(yǔ)言本身有效的集成在了一起。下面這篇文章將給大家介紹關(guān)于利用Golang解析json數(shù)據(jù)的方法,文中給出了詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-07-07goland 設(shè)置project gopath的操作
這篇文章主要介紹了goland 設(shè)置project gopath的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05初探Golang數(shù)據(jù)結(jié)構(gòu)之Slice的使用
在學(xué)習(xí)Go語(yǔ)言時(shí),一直對(duì)數(shù)組和切片的使用場(chǎng)景好奇,不明白為什么推薦使用切片來(lái)代替數(shù)組,所以本文就來(lái)和大家梳理一下Slice切片的相關(guān)知識(shí)吧2023-09-09