Go語言的複合資料型別
來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。# Go語言的複合資料型別 #***Go語言的複合資料型別是基礎資料類型的組合,主要包括四個**數組,切片(slice),map和結構體**。數組和結構體的大小是固定大小的,數組的元素類型是固定的,結構體的元素類型是不固定。map和slice是動態資料結構,它們將根據需求動態增長。***##數組數組顧名思義就是同一類資源或者資料的集合。下面主要介紹對數組的操作:**數組的初始化**var arr [3]int //預設初始化0var q [3]int = [3]int{1,2,3}q := [...]int{1,2,3}q := [...]int{90:-1}//key和value的賦值方式,下表90的值為-1,數組長度為91**數組的訪問**可以使用數組下標來訪問數組中的元素。與C語言相同,數組下標從0開始,len(array)-1則表示最後一個元素的下標。下面的樣本遍曆整型數組並逐個列印元素內容:q := [...]int{1, 2, 3, 4}for i, value := range q {fmt.Println(i, value)}數組可以直接進行比較,當數組內的元素都一樣的時候表示兩個數組相等。arr1 := [3]int{1, 2, 3}arr2 := [3]int{1, 2, 3}arr3 := [3]int{1, 2, 4}fmt.Println(arr1 == arr2, arr1 == arr3) //true,false數組可以作為函數的參數傳入,但由於數組在作為參數的時候,其實是進行了拷貝,這樣在函數內部改變數組的值,是不影響到外面的數組的值得。func ArrIsArgs(arr [4]int) {arr[0] = 100}q := [...]int{1, 2, 3, 4}ArrIsArgs(q)如果想要改變:func ArrIsArgs(arr *[4]int) {arr[0] = 100}q := [...]int{1, 2, 3, 4}ArrIsArgs(&q)但一般都是用切片來解決這個問題,而不是用數組。**切片**切片和數組很相似,只是它的長度並不是固定的,也就是定義的時候[ ]T內不需要指定長度。數組和slice關係非常密切,一個slice可以訪問數組的部分或者全部資料,而且slice的底層本身就是對數組的引用。一個Slice由三部分組成:指標,長度和容量。內建的len和cap函數可以分別返回slice的長度和容量首先我們來看slice的建立方式:建立slice主要兩種:1.基於數組建立。2.直接建立1. 基礎數組建立:arrVar := [4]int{1, 2, 3,4}sliceVar := arrVar[1:3]數組arrVar和sliceVar裡面的地址其實是一樣的,也就是說如果你改變sliceVar裡面的變數,那麼arrVar裡面的變數也會隨之改變。2.直接建立可以使用內建的make()函數來建立。事實上還是會建立一個匿名的數組,只是不需要我們來定義。myslice1 := make([ ]int,5)//建立一個元素個數5的slice,cap也是5myslice2 := make([ ]int,5,10)//建立一個元素個數5的slice,cap是10myslice3 := [ ]int{1,2,3,4}//建立一個元素個數為4的slice,cap是4var slice []int //建立一個空的slice,cap和len都是0對於為什麼說slice其實和數組是一個地址那,看下面這張圖:3.動態增減元素前面說過,slice是可以動態擴充的。但slice的動態擴充是有代價的,也就是說如果在確定大小的前提下,最好是設定好slice的cap大小,看個經典的例子:func TestAppend() {var slice []intfor i := 0; i < 10; i++ {slice = append(slice, i)fmt.Printf("%d cap = %d t%v\n", i, cap(slice), slice)}}output:0 cap = 1 t[0]1 cap = 2 t[0 1]2 cap = 4 t[0 1 2]3 cap = 4 t[0 1 2 3]4 cap = 8 t[0 1 2 3 4]5 cap = 8 t[0 1 2 3 4 5]6 cap = 8 t[0 1 2 3 4 5 6]7 cap = 8 t[0 1 2 3 4 5 6 7]8 cap = 16 t[0 1 2 3 4 5 6 7 8]9 cap = 16 t[0 1 2 3 4 5 6 7 8 9]可以看到,當slice的的容量等於len的時候,cap是翻倍了。append的底層原理就是當slice的容量滿了的時候,重建立立一塊記憶體,然後將原來的資料拷貝到建立的記憶體。所以說容量的擴充是存在記憶體的建立和複製的。該過程將會影響到系統的運行速度.append 也可以將兩個slice拼接起來,但格式有所不同:oldSlice := []int{1, 2, 3, 4, 5}oldSlice2 := []int{6, 7, 8, 9, 10}oldSlice = append(oldSlice, oldSlice2...) //必須加三個...兩個slice也可以直接進行內容的複製,copy函數:slice1 := []int{1, 2, 3, 4, 5}slice2 := []int{5, 4, 3}copy(slice2, slice1) // 只會複製slice1的前3個元素到slice2中copy(slice1, slice2) // 只會複製slice2的3個元素到slice1的前3個位置4.作為形參使用要想搞清楚slice我們需要看一下slice作為函數參數的使用方式,看一下下面的幾個例子:func TestSLice(myslice []int) {for i := 0; i < 8; i++ {myslice = append(myslice, i)}}eg1:myslice1 := make([]int, 8, 8)TestSLice(myslice1[:0])fmt.Println(myslice1) //結果是什嗎?eg2:myslice1 := make([]int, 8, 8)TestSLice(myslice1 )fmt.Println(myslice1) //結果是什麼哪?eg1 的結果是[0 1 2 3 4 5 6 7],eg2的結果是[0 0 0 0 0 0 0 0]。來分析兩個執行個體的不同,如果和你想的不一樣,那麼就證明你對slice的理解是有問題的(我也糾結了比較久o(╯□╰)o)首先分析一下第一個用例TestSLice(myslice1[:0]),這個的意思其實就是將一個新的slice作為參數傳給函數,新的slice指向的是myslice1的第0個元素的地址,這時候你可以測試一下myslice1[:0]這個東西的cap和len,數值分別為8和0,然後你在函數裡面append的時候實際上是對新的slice進行了操作,但由於外面的函數的參數的slice和myslice1又是同一個地址,所以外面的數值是被改變了的。再來分析第二個執行個體,第二個執行個體直接把myslice1傳給了函數,這其實是複製了一個slice的,在函數裡面append的時候當你的len大於cap值得時候,這時候返回的是一個新的地址。所以外面的變數是不會變化的。全是預設值0那麼我們想直接改變外面的slice怎麼辦?答案就是指標,**在GO語言裡面你想修改什麼就傳什麼的指標**。func TestSLice3(myslice *[]int) {for i := 0; i < 8; i++ {*myslice = append(*myslice, i)}}myslice1 := make([]int, 0, 8)TestSLice3(&myslice1)對於SLICE的應用基本就可以到這裡結束了,大家可以多測試一下Slice的使用。**在Go語言中,所有的函數參數都是值拷貝傳入的,函數參數將不再是函數調用時的原始變數******##map在C++/java中,map 一般都是封裝在庫裡面的,但在GO語言中map可以直接使用。作為Key-value的資料結構,map就是對雜湊表的引用。1.聲明var myMap map[string] PersonInfomyMap是聲明的變數名,sting是對應的Key的類型,PeesonInfo是value的類型。2.建立建立map使用的是GO語言內建的make()來建立的。myMap = make(map[string] Personinfo)建立一個容量固定的MAPmyMap = make(map[string] Personinfo,100)建立初始化MAPmymap1 := map[string]int{"hello": 1, "world": 2}3.元素的刪除對於map的元素的刪除,可以採用內建的delete函數delete(myMap,"123")如果你後面傳入的key不存在,那麼調用不會產生什麼錯誤,但如果myMap是nil那麼就會拋出異常了。4.元素的尋找在map中傳統的做法是:1.聲明一個變數為空白2.將map中獲得的值儲存到變數中3.判斷是否為空白。但這種做法過於複雜,可以採用下面的方式來實現:value,ok := myMap["1234"]if ok{//代表找到了//代碼處理模組}***##結構體結構體是一種彙總的資料類型,是由零個或多個任意類型的值彙總成的實體。所以結構體裡面不可以包含結構體類型的資料,但是可以包含該結構體類型的指標。1.結構體的定義:type MyStruct struct{name string age intmale string}var mystruct1 = myStruct{"wenxuwan", 12, "man"}var mystruct2 = myStruct{name: "mashijie", age: 12, male: "man"}**結構體字面值並沒有簡短表示匿名成員的文法**2.結構體的操作type myStruct struct {name stringage intmale string}var mystest myStruct //定義structmystest.name = "wenxuwan" //通過.操作賦值mystest.age = 28mystest.male = "man" var myStructPoint *myStruct = &mystest //通過指標操作結構體memberPoint := &mystest.age //可以對裡面的成員變數取值,然後用指標進行操作*memberPoint = 200 fmt.Println(myStructPoint.name, myStructPoint.age, myStructPoint.male)3.結構體的嵌入和匿名函數首先我們需要來看為啥結構體是需要嵌套,看下面的例子type Bird struct {method stringspecies stringmale string}type tiger struct {method stringspecies stringmale stringyanchi int //牙齒專屬假設}上面的兩個結構體,都包含了三個相同的變數,當然也有不同的,如果再加上其它的動物,那麼我們需要寫很多重複的代碼,這樣費時費力。所以我們可以寫成以下方式:type Animal struct {method stringspecies stringmale string}type Bird struct {animal Animalfly bool}type Tiger struct {animal Animalyanchi int}var tigers Tigertigers.animal.male = "gong"tigers.animal.species = "maoke"tigers.animal.method = "run"這種方式是不是和當年我們剛開始學物件導向的繼承的時候一樣的感覺。迴歸到這個問題,改動之後結構體類型變的清晰了,但是這種修改同時也導致了訪問每個成員變得繁瑣。**Go語言有一個特性讓我們只聲明一個成員對應的資料類型而不指名成員的名字;這類成員就叫匿名成員。匿名成員的資料類型必須是命名的類型或指向一個命名的類型的指標**type Bird struct {Animalfly bool}type Tiger struct {Animalyanchi int}var tigers Tigertigers.male = "nan"tigers.method = "pao"如果你的Tiger裡面也有Animal裡面的變數,那麼此處更改的是tiger的變數而不是Animal裡面的。238 次點擊 ∙ 1 贊