Golang Slice

[toc]

什么是 Slice

切片是對數(shù)組的抽象,提供動(dòng)態(tài)數(shù)組的能力。切片的長度是不固定的,隨著元素的增加而動(dòng)態(tài)變化。

數(shù)組與 Slice 的區(qū)別

數(shù)組

  • 值類型
  • 長度固定
  • 編譯期間檢查下標(biāo)訪問越界行為
  • 可以作為 map 的 key

Slice

  • 引用類型
  • 長度可變
  • 運(yùn)行期間檢查下標(biāo)訪問越界行為
  • 不可以作為 map 的 key

適用場景

  • 長度不固定
  • 參數(shù)傳遞

用法示例

var nums []int               // 聲明切片
var arr = [3]int{4, 5, 6}    // 初始化數(shù)組
nums = append(nums, 1, 2, 3) // 切片追加元素
l := len(nums)               // 獲取元素個(gè)數(shù)
c := cap(nums)               // 獲取切片容量
bigger := make([]int, 6)     // 創(chuàng)建指定長度和容量的切片
copy(bigger, nums[:])        // 將 nums 中所有元素拷貝到 bigger 切片中
copy(bigger[3:], arr[:])     // 將 arr 數(shù)組轉(zhuǎn)變成切片并拷貝到 bigger 切片從下標(biāo) 3 開始的地方
fmt.Println(bigger)          // 打印出 [1 2 3 4 5 6]

避坑指南【Bad Case】

  1. append 數(shù)據(jù)丟失
func main() {
    slice := make([]int, 0)
    slice = append(slice, 1, 2, 3)
    fmt.Println(slice) // 輸出: [1 2 3]
    updateSlice(slice, 1, -2)
    fmt.Println(slice) // 輸出: [1 -2 3]
    appendSlice(slice, 2)
    fmt.Println(slice) // 輸出: [1 -2 3]
}

func updateSlice(slice []int, idx int, val int) {
    slice[idx] = val
    fmt.Println(slice) // 輸出: [1 -2 3]
}

func appendSlice(slice []int, val int) {
    slice = append(slice, val) // 擴(kuò)容
    fmt.Println(slice) // 輸出: [1 -2 3 2]
}
  1. 并發(fā) append 數(shù)據(jù)丟失
func main() {
    slice := make([]int, 0)
    var wg sync.WaitGroup
    var appendSlice = func(val int) {
        slice = append(slice, val)
        wg.Done()
    }
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go appendSlice(i)
    }
    wg.Wait()
    fmt.Printf("len = %d, cap = %d", len(slice), cap(slice)) // len <= 10, cap <= 16
}

數(shù)據(jù)結(jié)構(gòu)

type slice struct {
    array unsafe.Pointer // 指針指向 heap 區(qū)一塊連續(xù)內(nèi)存地址
    len   int  // 長度,實(shí)際元素個(gè)數(shù)
    cap   int // 容量,最多元素個(gè)數(shù)
}
image.png

擴(kuò)容機(jī)制

當(dāng) Slice 容量不足以 append 新的元素時(shí),會(huì)觸發(fā)擴(kuò)容。擴(kuò)容時(shí)會(huì)申請一塊新的內(nèi)存空間,然后將舊 Slice 數(shù)據(jù)拷貝到新 Slice,并 append 新元素。為避免頻繁擴(kuò)容,擴(kuò)容規(guī)則如下:

  1. 當(dāng)新容量大于 2 倍舊容量時(shí),按實(shí)際容量擴(kuò)容
  2. 否則當(dāng)舊容量小于 1024 時(shí),按 2 倍容量擴(kuò)容
  3. 否則按舊容量的 1.25 倍容量擴(kuò)容

另外,為考慮內(nèi)存對齊,避免內(nèi)存浪費(fèi),最終的容量可能會(huì)基于上面規(guī)則計(jì)算的值進(jìn)行微調(diào),具體細(xì)節(jié)可以閱讀源碼

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

相關(guān)閱讀更多精彩內(nèi)容

  • 用new聲明slice時(shí)1.new出來的分片變量,只有一個(gè)地址,還沒有底層數(shù)組,如果直接賦值會(huì)panic,這時(shí)候需...
    zzz1t1閱讀 334評論 0 0
  • 看完這篇文章,下面這些高頻面試題你都會(huì)答了吧 Go slice的底層實(shí)現(xiàn)原理 Go array和slice的區(qū)別 ...
    Go程序員閱讀 2,160評論 0 4
  • 環(huán)境 初始化 下標(biāo) 字面量 關(guān)鍵字 Compile Time cmd/compile/internal/types...
    七秒鐘回憶待續(xù)閱讀 1,068評論 1 1
  • 本文基于golang 1.10版本分析。 slice 結(jié)構(gòu) slice實(shí)際就是一個(gè)struct,在runtime/...
    123archu閱讀 2,509評論 2 3
  • 前言 今天來說個(gè)簡單的,也不簡單的東西,那就是切片。slice對于golang來說那真的是一個(gè)非常常用的東西了,很...
    LinkinStar閱讀 362評論 0 1

友情鏈接更多精彩內(nèi)容