Go數組和切片你不知道的區別


開篇語

數組和切片是兩種不同的數據結構,比較常見,在Go語言中同時存在,今天我們就一起來看看他們在使用方式上,原理上的一些區別?

數組

在Go語言中,數組是一種具有相同類型固定大小的一種數據結構。

我們先來看看數組的使用,數組類型聲明時的方式是 []T ,前面的[]指定數組的大小,T指定數組的類型,如下我們聲明了一下數組,數組的大小是3,在沒指定數組初始值時數組默認初始值是{0,0,0}

array1 := [3]int{}
//我們可以通過如下方式給數組賦值
array1[0] = 1
array1[1] = 2
array1[2] = 3
//下面這種也是數組聲明的一種方式,並且初始化數組的值為{1,2}
array2 := [2]int {1,2}

思考一下前面我們array1賦值給array2對嗎?記住,這種方式是錯誤的,在Go語言中只有大小相等,類型相同的數組才是同類型的數組,之間才可以相互賦值,如下截圖,我們可以看見array1賦值給array2時編譯器報錯

array2 = array1

看上面的圖,我們再思考一個問題,我們把array3 賦值給array2后,修改了array3下標為0的值等於6,請問打印的結果是?這個問題考驗的是我們把array3賦值給array2后,修改了array3的值,會對array2產生影響嗎?答案是不會,記住,在Go中,數組屬於基本類型,他們之間的賦值,傳遞是屬於值拷貝,同樣的,如果將數組作為參數在函數間傳遞,也是屬於值拷貝

第一種結果:0,0,0;  4,5; 6,5
第二種結果:0,0,0;  6,5; 6,5

數組的長度,我們聲明了一個數組,那如何獲取數組的長度呢?通過len(array4)獲取數組的長度

array4 := [2]int {1,2}
l := len(array4)
fmt.Println(""l)

多維數組的聲明,多為數組可以想象成就是多個一維數組,如下聲明了一個二維數組,代表有兩個一維數組,每個一維數組的長度是2

array4 := [2][2]int{}
array6 := [2][2]int{{1,2},{3,4}}

對數組的訪問和遍歷

//訪問數組中的元素
array4 := [2]int {1,2}
//訪問數組下標為0處的值並打印
fmt.Println(array4[0])
//通過range遍歷數組,
//i代表數組的下標,
//v代表數組下標為i處的值
for i,v := range array4{
    fmt.Println(i,v)
}    
array5 := [2][2]int{{1,2},{3,4}}
for i,tempArray := range array5{
    //此時tempArray是一維數組
    //再通過range 遍歷tempArrayy一維數組
    for j,v := range tempArray{
        fmt.Println(j,v)
    }
}

切片

在Go語言中,切片是數組的一種高級運用,相對於數組,切片是一種更加方便,靈活,高效的數據結構。,切片並不存儲任何元素而只是對現有數組的引用(不是值拷貝,是指針)

切片的聲明方式有以下幾種

通過數組創建一個切片

array1 := [3]int{1,2,3}
//將數組下標從1處到下標2處的元素轉換為一個切片(前閉后開)
slice1 := array1[1:2]

直接聲明一個切片

//下面代碼直接出初始化一個切片 (這里大家有個疑問,我不管怎么看都覺得它是一個數組啊)
//記住,再go語言中,區別一個變量是數組還是切片,就看有沒有定義長度
//有定義長度就是數組,如array1,沒定義就是切片 如slice2
//我們也通過fmt.Println(reflect.TypeOf(array1),reflect.TypeOf(slice2))
//上面這代碼打印的結果是[3]int,[]int,可以看到前者有長度,后者沒有
slice2 := []int{1,2,3}

通過make函數創建一個切片,也是最常用的

//[]int,指定切片的類型,3是切片的長度,6是切片的容量
slice3 := make([]int,3,6)

通過切片生成一個切片

//聲明一個切片
slice4 := []int {1,2,3,4,5,6}
//通過slice4創建一個切片,元素是slice4下標從0到1(不包含1)的元素
slice5 := slice4[0:1] 
//通過slice4創建一個切片,元素是slice4下標從0到末尾的元素
slice6 := slice4[1:] 
 //通過slice4創建一個切片,元素是slice4下標從0到3的元素
slice7 := slice4[:3]

上面我們介紹了切片的幾種常見構造方式,接下來我們看看如何操作切片

slice3   := make([]int,3,6)
//給切片賦值
slice3[0] = 0
slice3[1] = 1
slice3[2] = 2
//通過len([]Type) cap([]Type)兩個函數查看切片的長度和容量
fmt.Println(len(slice3),cap(slice3))
結果:3,6

思考一下我們能給上面切片slice3下標為3處賦值嗎 ? slice3[3] = 3,答案是不能的,雖然我們定義了切片的長度是3,容量是6,但對切片的操作是以長度為准的,如果已經賦值到最大長度了,怎么辦呢?切片為我們提供了append([]Type, elems...Type)[]Type 方法向切片中追加元素 []Type代表傳入一個切片,elems代表追加的元素,可以傳多個。

//想slice3 切片追加三個元素,返回一個新的切片
slice3 = append(slice3,3,4,5)
//此時再次查看切片的長度和容量
fmt.Println(len(slice3),cap(slice3))
結果: 6, 6 切片的長度和容量保持一致了
//思考一下,我們再次append能給切片追加元素嗎? 肯定可以的,前面說過切片是可擴長的
slice3 = append(slice3,7,8,9)
//此時再次查看切片的長度和容量
fmt.Println(len(slice3),cap(slice3))
結果:9, 12
發現了什么?在切片容量滿時,切片的擴容時翻倍的,也就是新的切片的容量時原切片的容量的2倍

知道了切片的追加,長度,容量,那么如何刪除切片里的元素呢?如果你在看通過切片創建一個切片時思考過,你就知道如何刪除切片中的元素了

//我們創建了一個切片,有四個元素,下標命名為0,1,2,3
slice11 := []int{1,2,3,4} 
//假設我們要刪除下標為0的元素,這段代碼的含義是
創建一個空的切片 slice11[:0]
創建一個從下標1到末尾元素的切片 slice11[1:]
給空切片添加元素並返回一個新的切片
slice12 := append(slice11[:0],slice11[1:]...)
//同上我們得出刪除下標為i的元素的切片的公式時
sliceTemp := append(slice11[:i],slice[i+1:]...)

下圖是切片刪除的一個過程

數組和切片的底層存儲

我們看下圖分析

基於上圖我們會發現,切片的底層就是數組,切片是通過指針的形式指向不同數組的位置從而形成不同的切片,切片對本身元素的修改,也會影響到數組和其它的切片。看下面代碼,大家猜一猜輸出結果

array11 := [5]int{1,2,3,4,5}
slice11 := array11[0:3]
slice11[0] = 10
sliceTemp := append(array11[:2],array11[3:]...)
slice11[0] = 11
fmt.Println(array11,slice11,sliceTemp)
//輸出結果:[11 2 4 5 5] [11 2 4] [11 2 4 5]

歡迎大家關注微信公眾號:“技術人技術事”,更多精彩期待你的到來

![](https://img2018.cnblogs.com/blog/706455/201908/706455-20190821135940196-775543041.jpg)

,


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM