Go起步:4、複合類型1--數組array和切片slice

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

之前講到了Go的基礎資料類型,除此之外,Go還支援很多複合類型的資料結構。

數組(array)

數組就是指一系列同一類型資料 的集合。
Go語言中,類型 [n]T 表示擁有 n 個 T 類型的值的數組。如:

var a [3]int

表示變數 a 聲明為擁有有 3個整數的數組。聲明文法上與java的區別是[]是寫在類型前面的。
當然,也可以讓編譯器統計數組字面值中元素的數目:

a := [...]int{1, 2,3}

這兩種寫法, a 都是對應長度為3的int數組。
數組的長度是其類型的一部分,因此數組不能改變大小。 可以用內建函數len()取得數組長度。

package mainimport "fmt"func main() {    var a [2]string //定義一個長度為2的字串數組    a[0] = "Hello"  //下標1賦值為Hello    a[1] = "World"    fmt.Println(a[0], a[1]) //按下標取值    fmt.Println(a)          //列印數組    primes := [...]int{2, 3, 5, 7, 11, 13} //定義一個長度為6的int數組,並初始化    for i := 0; i < len(primes); i++ {        fmt.Println(primes[i])    }}


從上面可以看出,數組訪問和賦值可以用下標的方式,下標從0開始,這點和其他大部分程式設計語言一致。
Go的數組也支援多維陣列。定義方式如下:

var arrayName [ x ][ y ] variable_type
package mainimport "fmt"func main() {    a := [3][4]int{{0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11}}    fmt.Println(a)    for i := 0; i < 3; i++ {        for j := 0; j < 4; j++ {            fmt.Printf("a[%d][%d] = %d\n", i, j, a[i][j])        }    }}


上面展示了二維數組的定義初始化和取值。
特別需要說明的一點是,初始化時的最後兩個引號不能分行寫,否則編譯會不過,Go編譯器不知為何做這種限制。如下寫法是錯誤的。

a := [3][4]int{{0, 1, 2, 3}, {4, 5, 6, 7},     {8, 9, 10, 11}}

切片(slice)

前面說過,數組的長度是不可變的,這在操作上帶來了很大不便,但是Go給出了很好的解決方案,就是切片(slice)。
Go的切片是對數組的抽象。Go數組的長度不可改變,在特定情境中這樣的集合就不太適用,Go中提供了一種靈活,功能強悍的內建類型切片(“動態數組”),與數組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。

定義

可以通過聲明一個未指定大小的數組來定義切片,類型 []T 表示一個元素類型為 T 的切片。從這個角度來說,切片可以視為動態大小的數組。
但是,切片並不儲存任何資料, 它只是描述了底層數組中的一段。更改切片的元素會修改其底層數組中對應的元素。與它共用底層數組的切片都會觀測到這些修改

var s []type

除此之外,可以使用make()函數來建立切片:

var slice1 []type = make([]type, length ,capacity)

其中type是切片的類型,length是切片的初始化長度,capacity是選擇性參數,指切片容量。
make 函數會分配一個元素為零值的數組並返回一個引用了它的切片。

a := make([]int, 5)     // len(a)=5, cap(a)=5b := make([]int, 0, 5)  // len(b)=0, cap(b)=5

len()函數可以返回切片的長度,cap()函數返回切片的容量。

初始化

切片初始化是很靈活的,方法也有很多種。
1、直接初始化切片,[]表示是切片類型,{1,2,3}初始化值依次是1,2,3.其cap=len=3

s :=[] int {1,2,3 } 

2、初始化切片s,是數組arr的引用

s := arr[:] 

3、將arr中從下標startIndex到endIndex-1 下的元素建立為一個新的切片,arr可以是數組也可以是一個切片,這是定義的切片就是切片的切片。

s := arr[startIndex:endIndex] 

4、預設endIndex時將表示一直到arr的最後一個元素

s := arr[startIndex:] 

5、預設startIndex時將表示從arr的第一個元素開始

s := arr[:endIndex] 

6、通過內建函數make()初始化切片s,[]int 標識為其元素類型為int的切片

s :=make([]int,len,cap) 
package mainimport "fmt"func main() {    //1、直接初始化切片    var s1 = []int{1, 2, 3, 4, 5}    s11 := []int{1, 2, 3, 4, 5}    //2、初始化切片s,是數組arr的引用    var arr = []int{1, 2, 3, 4, 5}    s2 := arr[:]    //3、從下標startIndex到endIndex-1 下的元素建立為一個新的切片    s3 := arr[1:3]    s31 := s1[1:3]    //4、預設endIndex時將表示一直到arr的最後一個元素    s4 := arr[3:]    s41 := s1[3:]    //5、預設startIndex時將表示從arr的第一個元素開始    s5 := arr[:4]    s51 := s1[:4]    //6、通過內建函數make()初始化切片s,[]int 標識為其元素類型為int的切片    s6 := make([]string, 4, 50)    s6[0] = "a"    s6[1] = "b"    s6[2] = "c"    s6[3] = "d"    s61 := make([]string, 4)    fmt.Println("s1:", s1)    fmt.Println("s11:", s11)    fmt.Println("s2:", s2)    fmt.Println("s3:", s3)    fmt.Println("s31:", s31)    fmt.Println("s4:", s4)    fmt.Println("s41:", s41)    fmt.Println("s5:", s5)    fmt.Println("s51:", s51)    fmt.Println("s6:", s6)    fmt.Println("len(s6):", len(s6))    fmt.Println("cap(s6)", cap(s6))    fmt.Println("s61:", s61)    fmt.Println("len(s61):", len(s61))    fmt.Println("cap(s61)", cap(s61))}

輸出為:

空(nil)切片

上面是對於切面的初始化,在一個切片在未初始化之前預設為 nil,長度為 0且沒有底層數組。。nil是Go的一個關鍵字。

package mainimport "fmt"func main() {    var s []int    fmt.Println(s, len(s), cap(s))    if s == nil {        fmt.Println("nil!")    }}


可以看出,切片s長度和容量都是0,值是nil。即切片是空的。

切片的內幕

一個切片是一個數組片段的描述。它包含了指向數組的指標,片段的長度, 和容量(片段的最大長度)。
切片操作並不複製切片指向的元素。它建立一個新的切片並複用原來切片的底層數組。 這使得切片操作和數組索引一樣高效。因此,通過一個新切片修改元素會影響到原始切片的對應元素。

package mainimport "fmt"func main() {    s1 := [...]int{1, 2, 3, 4, 5}    s2 := s1[2:]    fmt.Println("修改前s1:", s1)    fmt.Println("修改前s2:", s2)    s2[2] = 10    fmt.Println("修改後s2:", s2)    fmt.Println("修改後s1:", s1)}

切片的增長

前面說過,切片可以看成是動態數組,所以他的長度是可變的。只要切片不超出底層數組的限制,它的長度就是可變的,只需將它賦予其自身的切片即可。

package mainimport "fmt"func main() {    s := make([]int, 5, 10)    fmt.Println("修改後s:", len(s))    s = s[:cap(s)]    fmt.Println("修改後s:", len(s))}


上面就是把切片s的長度修改成他的最大長度。如果超過他的最大長度,則會報錯–“panic: runtime error: slice bounds out of range”。

s = s[:12]


如果想增加切片的容量,我們必須建立一個新的更大的切片並把原分區的內容都拷貝過來。整個技術是一些支援動態數組語言的常見實現。

package mainimport "fmt"func main() {    s := make([]int, 5, 10)    t := make([]int, len(s), cap(s)*2) // 擴大s的容量    for i := range s {        s[i] = i        t[i] = s[i]    }    fmt.Println("修改前s:", s)    fmt.Println("修改前len(s):", len(s))    fmt.Println("修改前cap(s):", cap(s))    s = t    fmt.Println("修改後s:", s)    fmt.Println("修改後len(s):", len(s))    fmt.Println("修改後cap(s):", cap(s))}


上面把一個切片的容量擴大了2倍。
對於迴圈中複製的操作Go提供了可copy內建函數。copy函數可以將源切片的元素複製到目的切片。copy函數支援不同長度的切片之間的複製(它只複製較短切片的長度個元素)。此外, copy 函數可以正確處理源和目的切片有重疊的情況。
使用copy函數可以直接替換上面的for迴圈。

copy(t, s)

除此之外,Go還提供了一個為切片追加新的元素操作的方法– append()。
append 的第一個參數 s 是一個元素類型為 T 的切片, 其餘類型為 T 的值將會追加到該切片的末尾。append 的結果是一個包含原切片所有元素加上新添加元素的切片。

package mainimport "fmt"func main() {    var s []int    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)    s = append(s, 0)    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)    s = append(s, 1)    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)    s = append(s, 2, 3, 4)    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)    s2 := []int{5, 6, 7}    s = append(s, s2...)    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)}


上面的程式,先建立了一個nil切片,然後不斷往裡面添加新的資料。s = append(s, s2…)這個寫法是把後面的s2切片打散傳給append,相當於是s = append(s, 5, 6, 7),這也是Go支援的文法。
可以看出切片的長度和容量是不斷增加的。通過我的觀察,append增加容量是按照如果容量不夠把之前切片的容量乘以2,如果乘以2還不夠就之前容量+1乘以2來遞增的。不過這個以後還得看看源碼確認下,今天一直沒找到在哪。

通過到目前的瞭解,切片應該在Go中使用的比數組要多。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.