當前位置:
首頁 > 知識 > 詳解go語言的array和slice「一」

詳解go語言的array和slice「一」

本篇會詳細講解go語言中的array和slice,和平時開發中使用他樣時需要注意的地方,以免入坑。

Go語言中array是一組定長的同類型數據集合,並且是連續分配內存空間的。


聲明一個數組

var arr [3]int

數組聲明後,他包含的類型和長度都是不可變的.如果你需要更多的元素,你只能重新創建一個足夠長的數組,並把原來數組的值copy過來。

在Go語言中,初始化一個變數後,默認把變數賦值為指定類型的zero值,如string 的zero值為""
number類型的zero值為0.數組也是一樣的,聲明完一個數組後,數組中的每一個元素都被初始化為相應的zero值。如上面的聲明是一個長度為5的int類型數組。數組中的每一個元素都初始化為int類型的zero值
0

詳解go語言的array和slice「一」

可以使用array的字面量來快速創建和初始化一個數組,array的字面量允許你設置array的長度和array中元素的值


arr := [3]{1, 2, 3}

如果[]中用...來代替具體的長度值,go會根據後面初始化的元素長度來計算array的長度


arr := [...]{1, 2, 3, 4}

如果你想只給某些元素賦值,可以這樣寫


arr := [5]int {1: 5, 3: 200}

上面的語法是創建了一個長度為5的array,並把index為1的元素賦值為0,index為3的元素賦值為200,其他沒有初始化的元素設置為他們的zero值

詳解go語言的array和slice「一」

指針數組

聲明一個包含有5個整數指針類型的數組,我們可以在初始化時給相應位置的元素默認值。下面是給索引為0的元素一個新建的的int類型指針(默認為0),給索引為1的元素指向值v的地址,剩下的沒有指定默認值的元素為指針的zero值也就是nil

var v int = 6

array := [5]*int{0: new(int), 1: &v}

fmt.Println(len(array))

fmt.Println(*array[0])

fmt.Println(*array[1])

v2 := 7

array[2] = &v2

fmt.Println("------------------")

for i, v := range array {

fmt.Printf("index %d, address %v value is ", i, v)

if v != nil {

fmt.Print(*v)

} else {

fmt.Print("nil")

}

fmt.Println(" ")

}

詳解go語言的array和slice「一」


數組做為函數參數

比如我個創建一個100萬長度的int類型數組,在64位機上需要在內存上佔用8M的空間,把他做為一個參數傳遞到一個方法內,go會複製這個數組,這將導致性能的下降。

package main

import (

"fmt"

)

const size int = 1000*1000

func sum(array [size]int) float64 {

total := 0.0

for _, v := range array {

total += float64(v)

}

return total

}

func main() {

var arr [size]int

fmt.Println(sum(arr))

}

當然go也提供了其他的方式,可以用指向數組的指針做為方法的參數,這樣在傳參的時候會傳遞array的地址,只需要複製8個位元組,

package main

import (

"fmt"

)

const size int = 1000*1000

func sum(array *[size]int) float64 {

total := 0.0

for _, v := range array {

total += float64(v)

}

return total

}

func main() {

var arr [size]int

fmt.Println(sum(&arr))

}


slice

slice可以被認為動態數組,在內存中也是連續分配的。他可以動態的調整長度,可以通過內置的方法append來自動的增長slice長度;也可以通過再次切片來減少slice的長度。

slice的內部結構有3個欄位,分別是維護的底層數組的指針,長度(元素個數)和容量(元素可增長個數,不足時會增長),下面我們定義一個有2個長度,容量為5的slice

func main() {

s := make([]int, 2, 5)

fmt.Println("len: ", len(s))

fmt.Println("cap: ", cap(s))

s = append(s, 2)

fmt.Println("--------")

fmt.Println("len: ", len(s))

fmt.Println("cap: ", cap(s))

fmt.Println("--------")

s[0] = 12

for _, v := range s {

fmt.Println(v)

}

}

初始化slice後,他的長度為2,也就是元素個數為2,因為我們沒有給任何一個元素賦值,所以為int的zero值,也就是0.可以用len和cap看一下這個slice的長度和容量。

用append給這個slice添加新值,返回一個新的slice,如果容量不夠時,go會自動增加容易量,小於一1000個長度時成倍的增長,大於1000個長度時會以1.25或者25%的位數增長。

上面的代碼執行完後,slice的結構如下

詳解go語言的array和slice「一」


Slice 的聲明和初始化

創建和初始化一個slice有幾種不同的方式,下面我會一一介紹

使用make聲明一個slice

slice1 := make([]int, 3)

fmt.Println("len: ", len(slice1), "cap: ", cap(slice1), "array :",
slice1)

slice1 = append(slice1, 1)

fmt.Println("len: ", len(slice1), "cap: ", cap(slice1), "array :",
slice1)

make([]int, 3)
聲明了個長度為3的slice,容量也是3。下面的append方法會添加一個新元素到slice里,長度和容量都會發生變化。

輸出結果:

len: 3 cap: 3 array : [0 0 0]

len: 4 cap: 6 array : [0 0 0 1]

也可以通過重載方法指定slice的容量,下面:

slice2 := make([]int, 3, 7)

fmt.Println("len: ", len(slice2), "cap: ", cap(slice2), "array :",
slice2)

輸出長度為3,容量為7


使用slice字變數

使用字變數來創建Slice,有點像創建一個Array,但是不需要在[]指定長度,這也是Slice和Array的區別。Slice根據初始化的數據來計算度和容量

創建一個長度和容量為5的Slice

slice3 := []int{1, 2, 3, 4, 5}

fmt.Println("len: ", len(slice3), "cap: ", cap(slice3), "array :",
slice3)

也可以通過索引來指定Slice的長度和容量,

下面創建了一個長度和容量為6的slice

slice4 := []int{5: 0}

fmt.Println("len: ", len(slice4), "cap: ", cap(slice4), "array :",
slice4)

聲明nil Slice,內部結構的指針為nil。可以用append給slice填加新的元素,內部的指針指向一個新的數組

var slice5 []int

fmt.Println("len: ", len(slice5), "cap: ", cap(slice5), "array :",
slice5)

slice5 = append(slice5, 4)

fmt.Println("len: ", len(slice5), "cap: ", cap(slice5), "array :",
slice5)

詳解go語言的array和slice「一」

創建空Slice有兩種方式

slice6 := []int{}

fmt.Println("len: ", len(slice6), "cap: ", cap(slice6), "array :",
slice6)

slice6 = append(slice6, 2)

fmt.Println("len: ", len(slice6), "cap: ", cap(slice6), "array :",
slice6)

slice7 := make([]int, 0)

fmt.Println("len: ", len(slice7), "cap: ", cap(slice7), "array :",
slice7)

slice7 = append(slice7, 7)

fmt.Println("len: ", len(slice7), "cap: ", cap(slice7), "array :",
slice7)

slice的切片

// 創建一個容量和長度均為6的slice

slice1 := []int{5, 23, 10, 2, 61, 33}

// 對slices1進行切片,長度為2容量為4

slice2 := slice1[1:3]

fmt.Println("cap", cap(slice2))

fmt.Println("slice2", slice2)

slice1的底層是一個容量為6的數組,slice2指底層指向slice1的底層數組,但起始位置為array的第一個元素也就是23.因為slices2從索引1開始的,所以無法訪問底層數組索引1之前的元素,也無法訪問容量之後的元素。可以看下圖理解一下。

詳解go語言的array和slice「一」

新創建的切片長度和容量的計算

對於一個新slice[x:y] 底層數組容量為z,

x: 新切片開始的元素的索引位置,上面的slice1[1:3]中的1就是起始索引

y:新切片希望包含的元素個數,上面的slice1[1:3],希望包含2個底層數組的元素 1+2=3

容量: z-x 上面的slice1[1:3] 底層數組的容量為6, 6-1=5所以新切片的容量為5


修改切片導致的後果

由於新創建的slice2和slice1底層是同一個數組,所以修改任何一個,兩個slice共同的指向元素,會導致同時修改的問題

詳解go語言的array和slice「一」

// 創建一個容量和長度均為6的slice

slice1 := []int{5, 23, 10, 2, 61, 33}

// 對slices1進行切片,長度為2容量為4

slice2 := slice1[1:3]

fmt.Println("cap", cap(slice2))

fmt.Println("slice2", slice2)

//修改一個共同指向的元素

//兩個slice的值都會修改

slice2[0] = 11111

fmt.Println("slice1", slice1)

fmt.Println("slice2", slice2)

詳解go語言的array和slice「一」

如圖所示

詳解go語言的array和slice「一」

需注意的是,slice只能訪問其長度範圍內的元素,如果超出長度會報錯。

除了修改共同指向元素外,如果新創建的切片長度小於容量,新增元素也會導致原來元素的變動。slice增加新元素使用內置的方法append。append方法會創建一個新切片。

詳解go語言的array和slice「一」

// 創建一個容量和長度均為6的slice

slice1 := []int{5, 23, 10, 2, 61, 33}

// 對slices1進行切片,長度為2容量為4

slice2 := slice1[1:3]

fmt.Println("cap", cap(slice2))

fmt.Println("slice2", slice2)

//修改一個共同指向的元素

//兩個slice的值都會修改

slice2[0] = 11111

fmt.Println("slice1", slice1)

fmt.Println("slice2", slice2)

// 增加一個元素

slice2 = append(slice2, 55555)

fmt.Println(slice1)

fmt.Println(slice2)

詳解go語言的array和slice「一」

詳解go語言的array和slice「一」

如果切片的容量足夠就把新元素合添加到切片的長度。如果底層的的數組容量不夠時,會重新創建一個新的數組並把現有元素複製過去。

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

fmt.Println("slice2 cap", cap(slice3))

slice3 = append(slice3, 5)

fmt.Println("slice2 cap", cap(slice3))

輸出的結果為:

slice2 cap 3

slice2 cap 6

容量增長了一倍。


控制新創建slice的容量

創建一個新的slice的時候可以限制他的容量

// 創建一個容量和長度均為6的slice

slice1 := []int{5, 23, 10, 2, 61, 33}

// 對slices1進行切片,長度為2容量為3

slice2 := slice1[1:3:4]

fmt.Println("cap", cap(slice2))

fmt.Println("slice2", slice2)

slice2長度為2容量為3,這也是通過上面的公式算出來的

長度:y-x 3-1=2

容量:z-x 4-1= 3

需要注意的是容量的長度不能大於底層數組的容量

詳解go語言的array和slice「一」

綠顏色表示slice2中的元素,黃顏色表示容量中示使用的元素。但是需要注意的是,我們修改或者增加slice2容量範圍內的元素個數依然會修改slice1。

// 創建一個容量和長度均為6的slice

slice1 := []int{5, 23, 10, 2, 61, 33}

// 對slices1進行切片,長度為2容量為3

slice2 := slice1[1:3:4]

fmt.Println("cap", cap(slice2))

fmt.Println("slice2", slice2)

//修改一個共同指向的元素

//兩個slice的值都會修改

slice2[0] = 11111

fmt.Println("slice1", slice1)

fmt.Println("slice2", slice2)

// 增加一個元素

slice2 = append(slice2, 55555)

fmt.Println(slice1)

fmt.Println(slice2)

詳解go語言的array和slice「一」

本文作者:李鵬 轉自:博客園


中公優就業 幫你成就職業夢:

IT教育專業培訓:https://www.ujiuye.com/

IT教育勤工儉學計劃:http://www.ujiuye.com/zt/jyfc/?wt.bd=lyh

大數據時代下做java開發工程師:https://www.ujiuye.com/zt/java/?wt.bd=lyh

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 IT優就業 的精彩文章:

數據分析-殘酷的世界
關於SVM數學細節邏輯的個人理解(一)
WEB跨域資源共享:Cross-origin Resource Sharing(CORS)
最簡單實用的JQuery實現banner圖中的text打字動畫效果!
初始原型鏈學習

TAG:IT優就業 |