一篇文章理解 golang 中切片與數組的關係

起始

在 golang 文檔中,對數組與切片有一些詳細的講解,本文主要講解數組與切片的關係golang

因爲是我的理解,可能有些誤差,煩請指正數組

數組

golang 的數組比較簡單,咱們理解幾個概念便可數據結構

  1. 數組是固定長度與容量,而且具備相同類型的一組值
  2. 此定義的數組長度爲 5 ,那麼容量也會固定爲 5
  3. 數組的索引都是從 0 開始的

記住,咱們在此定義了一個 int 類型的數組,長度容量均爲 5,在後面的切片講解中,咱們將對此數組進行切片app

//  此定義的數組長度爲 5 ,那麼容量也會固定爲 5
arr := [5]int{0, 1, 2, 3, 4}
//  數組 p = 0xc00001c0f0,arr = [0 1 2 3 4],len = 5,cap = 5
fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))
// 數組的索引都是從 0  開始的
// 0 1
fmt.Println(arr[0], arr[1])

切片

切片的基礎概念

  1. 切片能夠看作是一個可變長的數組
  2. 切片能夠看作是對數組的一個片斷的引用

建立切片

咱們首先來建立一個對 arr 數組的一個切片code

[1:3] 表示範圍從數組的索引 1 至 數組的索引 3,它是不包括 3 的,能夠簡單理解爲 index == 1 && index < 3
當切片生成後,索引默認也是從 0 開始, 切片索引是 [0,1] 對應着數組的索引是 [1,2] 索引

arrSlice := arr[1:3]
// 切片 p = 0xc00000c060,arr = [1 2],len = 2,cap = 4
fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))

修改數組,切片會發生什麼?

咱們上面有提到過,切片是對數組的一個引用,那麼咱們修改數組的值,切片會發生什麼呢?文檔

// 把數組 index = 1 的值修改成 10
arr[1] = 10

// 切片 p = 0xc00000c060,arr = [10 2],len = 2,cap = 4
fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice)

能夠看到,咱們切片對應的值也被修改成 10,切片的底層數據結構中存在的底層數組是對咱們上面數組作的引用傳遞,關於引用傳遞和值傳遞你們應該仍是分的清的import

那麼對應的,咱們修改切片,數組也會發生相應的變化基礎

// 一樣的道理,修改切片的值也會影響到底層數組
arrSlice[0] = 8
// 切片 p = 0xc00000c060,arr = [8 2],len = 2,cap = 4
fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
// 數組 p = 0xc00001c0f0,arr = [0 8 2 3 4],len = 5,cap = 5
fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

對切片進行追加操做,會發生什麼?

上面說到,咱們修改切片或者是數組,都會有對應的變化,那麼咱們對切片進行追加呢?引用

// 對切片追加
arrSlice = append(arrSlice, 11)
// 切片 p = 0xc00008a020,arr = [8 2 11],len = 3,cap = 4
fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
// 數組 p = 0xc000090060,arr = [0 8 2 11 4],len = 5,cap = 5
fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

咱們能夠看到,數組的 index = 3 的值被修改成 11,我是這麼理解的:

  1. 追加前的切片索引是 [0,1] 對應着數組的 [1,2]
  2. 在這裏追加了一位,那麼切片的索引是 [0,1,2] 數組對應的索引是 [1,2,3]
  3. 因此追加的值,也會修改數組的值

切片的容量?

經過上面的代碼,咱們能夠看到咱們的 cap 一直都是 4,爲何呢?這就要涉及到一個容量的計算公式了

  1. 在建立切片時,咱們指定了對數組的 [1,3] 進行切片,這裏切出來的長度爲 2
  2. 切片在建立時,若是不指定容量,那麼容量會自動去計算,咱們這裏沒有指定容量,那麼他會自動計算
  3. 建立時計算容量的公式爲:建立時的長度 * 2,咱們的長度是 2 ,計算出來的容量則爲 4
  4. 在追加時,會先判斷容量夠不夠,若是容量足夠則容量不變;若是超出容量那麼判斷長度是否小於 1024 ,小於則容量 * 2,大於則容量 * 1.25

這就是爲何咱們的容量一直是 4 的緣由

切片容量超過原數組容量,會發生什麼?

咱們一直在反覆提,切片是對數組的引用,那麼當我切片已經超出數組的範圍,會發生什麼呢?

咱們寫代碼操做一下

arrSlice = append(arrSlice, 12, 13, 14)
// 切片 p = 0xc00000c060,arr = [8 2 11 12 13 14],len = 6,cap = 8
fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
// 數組 p = 0xc00001c0f0,arr = [0 8 2 11 4],len = 5,cap = 5
fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

在上面代碼中,咱們追加了三個值 12,13,14

咱們能夠看到,數組的值並無被修改,按照咱們上一步講的來講,應該數組的最後一位會變爲 12,可是並無
這是一個很重要的概念,當咱們的切片容量大於底層數組容量時,會自動建立一個新的底層數組,取消對原數組的引用

當咱們切片取消對原數組引用時,咱們再去修改切片,並不會影響到原數組

arrSlice[0] = 18
// 切片 p = 0xc00008c020,arr = [18 2 11 12 13 14],len = 6,cap = 8
fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
// 數組 p = 0xc000092060,arr = [0 8 2 11 4],len = 5,cap = 5
fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

被取消引用的數組去哪了?

根據查閱資料來看,大部分人都是說當被取消引用,而且也沒有別的地方去引用時,golang 的垃圾處理會自動回收;你們能夠具體嘗試一下

完整代碼

此文章完整代碼以下

// Enda <endachao@gmail.com>
package main

import "fmt"

func main() {
    //數組是固定長度與容量,而且具備相同類型的一組值
    //此定義的數組長度爲 5 ,那麼容量也會固定爲 5
    arr := [5]int{0, 1, 2, 3, 4}
    // 數組 p = 0xc00001c0f0,arr = [0 1 2 3 4],len = 5,cap = 5
    fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))
    // 數組的索引都是從 0  開始的
    // 0 1
    fmt.Println(arr[0], arr[1])

    // 切片能夠看作是一個可變長的數組
    // 切片能夠看作是對數組的一個片斷的引用
    // [1:3] 表示範圍從數組的索引 1 至 數組的索引 3,它是不包括 3 的,能夠簡單理解爲  index == 1 && index < 3
    // 當切片生成後,索引默認也是從 0 開始, 切片索引是 [0,1] 對應着數組的索引是 [1,2]
    arrSlice := arr[1:3]
    // 切片 p = 0xc00000c060,arr = [1 2],len = 2,cap = 4
    fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))

    // 因爲切片是對底層數據的一個引用,因此修改底層數組會更改切片的值
    arr[1] = 10
    // 切片 p = 0xc00000c060,arr = [10 2],len = 2,cap = 4
    fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))

    // 一樣的道理,修改切片的值也會影響到底層數組
    arrSlice[0] = 8
    // 切片 p = 0xc00000c060,arr = [8 2],len = 2,cap = 4
    fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
    // 數組 p = 0xc00001c0f0,arr = [0 8 2 3 4],len = 5,cap = 5
    fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

    // 對切片追加
    // 追加前的切片索引是 [0,1] 對應着 數組的 [1,2]
    // 在這裏追加了一位,那麼切片的索引是 [0,1,2] 數組對應的索引是 [1,2,3]
    // 因此追加的值,也會修改數組的值
    arrSlice = append(arrSlice, 11)
    // 切片 p = 0xc00008a020,arr = [8 2 11],len = 3,cap = 4
    fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
    // 數組 p = 0xc000090060,arr = [0 8 2 11 4],len = 5,cap = 5
    fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

    // 切片的容量爲何是 4
    // 在建立切片時,咱們指定了對數組的 [1,3] 進行切片,這裏切出來的長度爲 2
    // 切片在建立時,若是不指定容量,那麼容量會自動去計算
    // 建立時計算容量的公式爲:建立時的長度 * 2
    // 追加時,會先判斷長度夠不夠,若是夠則容量不變,若是不夠那麼判斷長度是否小於 1024 ,小於則容量 * 2,大於則 容量 * 1.25

    // 當咱們切片的長度超過了原數組的長度
    // 咱們能夠看到,數組的值並無被修改,按照咱們上一步講的來講,應該數組的最後一位會變爲 12,可是並無
    // 這是一個很重要的概念,當咱們的切片容量大於底層數組容量時,會自動建立一個新的底層數組
    arrSlice = append(arrSlice, 12, 13, 14)
    // 切片 p = 0xc00000c060,arr = [8 2 11 12 13 14],len = 6,cap = 8
    fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
    // 數組 p = 0xc00001c0f0,arr = [0 8 2 11 4],len = 5,cap = 5
    fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

    //當我再去修改切片的值時,並不會去操做咱們上面定義的數組了
    arrSlice[0] = 18
    // 切片 p = 0xc00008c020,arr = [18 2 11 12 13 14],len = 6,cap = 8
    fmt.Printf("切片 p = %p,arr = %+v,len = %d,cap = %d\n", &arrSlice, arrSlice, len(arrSlice), cap(arrSlice))
    // 數組 p = 0xc000092060,arr = [0 8 2 11 4],len = 5,cap = 5
    fmt.Printf("數組 p = %p,arr = %+v,len = %d,cap = %d\n", &arr, arr, len(arr), cap(arr))

}
相關文章
相關標籤/搜索