Golang 深入理解 Slice

參考鏈接: https://github.com/lvgithub/go_blog/blob/master/Books/slice.md

介紹

slice 是對數(shù)組的抽象,是對array的擴展,array的長度不可變,在特定場景中不太適用
slice 主要特點是不需要為它的容量擔心,可以追加元素,在追加時可能使切片的容量增大

slice 擴容

s := []int{1,2,3,4,5,6}
s = append(s, 6)
  • 如果新的slice大小是當前大小2倍以上,則大小增長為新大小
  • 如果當前slice cap 小于1024,按每次2倍增長,否則每次按當前大小1/4增長。直到增長的大小超過或等于新大小
  • append的實現(xiàn)是在內(nèi)存中將slice的array值賦值到新申請的array

性能

  • 通過上面我們知道slice的擴容涉及到內(nèi)存的拷貝,這樣帶來的好處是數(shù)據(jù)存儲在連續(xù)內(nèi)存上,比隨機訪問快很多,最直接的性能提升就是緩存命中率會高很多,這也就是為什么slice不采用動態(tài)鏈表實現(xiàn)的原因吧

  • 我們知道拷貝內(nèi)存數(shù)據(jù)是有開銷的, 而其中最大的開銷不在 memmove 數(shù)據(jù)上,而是在開辟一塊新內(nèi)存malloc及之后的GC壓力

  • 拷貝連續(xù)內(nèi)存是很快的,隨著cap變大,拷貝總成本還是 O(N) ,只是常數(shù)大了

  • 假如不想發(fā)生拷貝,那你就沒有連續(xù)內(nèi)存。此時隨機訪問開銷會是:鏈表 O(N)

  • 能知道所需的最大空間時,在make的時候預留相應(yīng)的 cap 就好

  • 如果需要的空間很大,且每次都不確定,那就要在浪費內(nèi)存和耗 CPU 在 malloc + gc 上做權(quán)衡

  • 鏈表的查找操作是從第一個元素開始,所以相對數(shù)組要耗時間的多,因為采用這樣的結(jié)構(gòu)對讀的性能有很大的提高

選擇

slice很靈活,大部分情況都能表現(xiàn)的很好
slice的容量超大并且需要頻繁的更改slice的內(nèi)容時,改用list更合適

舉例

s := []byte{1, 23, 4, 5, 67, 7}
s1 := s[2:3]
s1[0] = 100
fmt.Printf("s:%+v\n", s)
fmt.Printf("s[2] address is: %p\n", &s[2])
fmt.Printf("s1[1] address is: %p\n", &s1[0])
// s:[1 23 100 5 67 7]
// s[2]  address is: 0xc00007e004
// s1[1] address is: 0xc00007e004

沒錯,slice s 第三位的值4被替換為了100,這是因為slice s1 的底層array指針指向 slice s 的第三位,因此操作s1會影響切片s,因此賦值切片需要使用如下辦法:
temp := copy(dst, src)

  • 底層數(shù)組是可以被多個 slice 同時指向的
  • 基于slice 創(chuàng)建新 slice 對象,新、老 slice 共用底層數(shù)組,對底層數(shù)組的更改都會影響到彼此。
  • append可以掰斷新老slice共用底層數(shù)組的關(guān)系
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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