golang - slice

切片定義

切片是基于數組實現的,它的底層是數組,可以理解為對 底層數組的抽象。切片底層結構并沒有使用加鎖等方式,不支持并發(fā)讀寫,所以并不是線程安全的

源碼包中src/runtime/slice.go 定義了slice的數據結構:

type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

slice占用24個字節(jié)
- array: 指向底層數組的指針,占用8個字節(jié)
- len: 切片的長度,占用8個字節(jié)
- cap: 切片的容量,cap 總是大于等于 len 的,占用8個字節(jié)

slice有4種初始化方式

// 初始化方式1:直接聲明
var slice1 []int

// 初始化方式2:使用字面量
slice2 := []int{1, 2, 3, 4}

// 初始化方式3:使用make創(chuàng)建slice
slice3 := make([]int, 3, 5)         

// 初始化方式4: 從切片或數組“截取”
slcie4 := arr[1:3]
切片和數組的區(qū)別:
  • 數組長度不同

數組初始化必須指定長度,并且長度就是固定的

切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大

  • 函數傳參不同

數組是值類型,將一個數組賦值給另一個數組時,傳遞的是一份深拷貝,函數傳參操作都會復制整個數組數據,會占用額外的內存,函數內對數組元素值的修改,不會修改原數組內容。

切片是引用類型,將一個切片賦值給另一個切片時,傳遞的是一份淺拷貝,函數傳參操作不會拷貝整個切片,只會復制len和cap,底層共用同一個數組,不會占用額外的內存,函數內對數組元素值的修改,會修改原數組內容。

  • 計算數組長度方式不同

數組需要遍歷計算數組長度,時間復雜度為O(n)

切片底層包含len字段,可以通過len()計算切片長度,時間復雜度為O(1)

切片拷貝
  • 深拷貝
    拷貝的是數據本身,創(chuàng)造一個新對象,新創(chuàng)建的對象與原對象不共享內存,新創(chuàng)建的對象在內存中開辟一個新的內存地址,新對象值修改時不會影響原對象值, 深拷貝的方式有如下兩種
  1. copy(slice2, slice1)
func main() {
    slice1 := []int{1, 2, 3, 4, 5}
    slice2 := make([]int, 5, 5)
    fmt.Printf("slice1: %v, %p\n", slice1, slice1)
    copy(slice2, slice1)
    fmt.Printf("slice2: %v, %p", slice2, slice2)
}
  1. 遍歷append賦值
func main() {
    slice1 := []int{1, 2, 3, 4, 5}
    fmt.Printf("slice1: %v, %p\n", slice1, slice1)
    slice2 := make([]int, 5, 5)
    for i, v := range slice1 {
        slice2[i] = v
    }
    fmt.Printf("slice2: %v, %p", slice2, slice2)

上述兩種拷貝的方式,slice2 的地址與slice1 地址不同,所以是深拷貝

  • 淺拷貝
    拷貝的是數據地址,只復制指向的對象的指針,此時新對象和老對象指向的內存地址是一樣的,新對象值修改時老對象也會變化

引用類型的變量,默認賦值操作就是淺拷貝,例如如下的
slice2 := slice1

func main() {
    slice1 := []int{1, 2, 3, 4, 5}
    fmt.Printf("slice1: %v, %p\n", slice1, slice1)
    slice2 := slice1
    fmt.Printf("slice2: %v, %p", slice2, slice2)
}
//結果:slice1 和slice2 的底層地址相同,即指向同一個底層數組
slice1: [1 2 3 4 5], 0xc00001a120
slice2: [1 2 3 4 5], 0xc00001a120
擴容機制

擴容會發(fā)生在slice append的時候,當slice的cap不足以容納新元素,就會進行擴容,擴容規(guī)則如下

如果新申請容量比兩倍原有容量大,那么擴容后容量大小 為 新申請容量
如果原有 slice 長度小于 1024, 那么每次就擴容為原來的 2 倍
如果原 slice 長度大于等于 1024, 那么每次擴容就擴為原來的 1.25 倍

注意:切片發(fā)生擴容后,底層的數組指針會指向新的擴容后的數組

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 看完這篇文章,下面這些高頻面試題你都會答了吧 Go slice的底層實現原理 Go array和slice的區(qū)別 ...
    Go程序員閱讀 2,117評論 0 4
  • 本文基于golang 1.10版本分析。 slice 結構 slice實際就是一個struct,在runtime/...
    123archu閱讀 2,491評論 2 3
  • 寫個小demo來看看通過生成切片append擴容后發(fā)生了什么 打印信息如下 第一步我們創(chuàng)建了一個長度容量為5的sl...
    CappuccinoBx閱讀 2,657評論 0 1
  • 深入理解Golang Slice 數據結構 slice的底層數據結構中的array是一個指針,指向的是一個Arra...
    昔召閬夢閱讀 600評論 0 0
  • 首先我們來看段代碼的輸出 輸出的結果是 append的值5并沒有輸出,那么究竟是s0并不等價于s[0],還是有其他...
    Love語鬼閱讀 4,185評論 0 4

友情鏈接更多精彩內容