Go 切片相關(guān)面試題
來自軒脈刃的刀光劍影
這篇是關(guān)于go切片的一些問題和回答。
go的切片基本上是代碼中使用最多的一種數(shù)據(jù)結(jié)構(gòu)了,使用這種數(shù)據(jù)結(jié)構(gòu)有哪些要注意的點,這個是非常必要了解的東西?;旧?,以前寫的一篇博客 https://www.cnblogs.com/yjf512/p/9531282.html ?就說的很清楚了。這里再深挖一些。
問題:go的切片數(shù)據(jù)結(jié)構(gòu)是什么樣子的?
切片是有可能在編譯器就被內(nèi)聯(lián)的,而如果在編譯器沒有被內(nèi)聯(lián),進入運行期,就是直接使用SliceHeader數(shù)據(jù)結(jié)構(gòu)。
type?SliceHeader?struct?{
?Data?uintptr
?Len??int
?Cap??int
}
這三個字段分別表示指針,長度,容量。
問題:為什么在初始化slice的時候盡量補全cap
當我們要創(chuàng)建一個slice結(jié)構(gòu),并且往slice中append元素的時候,我們可能有兩種寫法來初始化這個slice。
方法1:
package?main
import?"fmt"
func?main()?{
?arr?:=?[]int{}
?arr?=?append(arr,?1,2,3,4,?5)
?fmt.Println(arr)
}
方法2:
package?main
import?"fmt"
func?main()?{
???arr?:=?make([]int,?0,?5)
???arr?=?append(arr,?1,2,3,4,?5)
???fmt.Println(arr)
}
方法2相較于方法1,就只有一個區(qū)別:在初始化[]int slice的時候在make中設(shè)置了cap的長度,就是slice的大小。
這兩種方法對應(yīng)的功能和輸出結(jié)果是沒有任何差別的,但是實際運行的時候,方法2會比少運行了一個growslice的命令。
這個我們可以通過打印匯編碼進行查看:
方法1:
image-20211219173237557方法2:
image-20211219174112164我們看到方法1中使用了growsslice方法,而方法2中是沒有調(diào)用這個方法的。
這個growslice的作用就是擴充slice的容量大小。就好比是原先我們沒有定制容量,系統(tǒng)給了我們一個能裝兩個鞋子的盒子,但是當我們裝到第三個鞋子的時候,這個盒子就不夠了,我們就要換一個盒子,而換這個盒子,我們勢必還需要將原先的盒子里面的鞋子也拿出來放到新的盒子里面。所以這個growsslice的操作是一個比較復(fù)雜的操作,它的表現(xiàn)和復(fù)雜度會高于最基本的初始化make方法。對追求性能的程序來說,應(yīng)該能避免盡量避免。
具體對growsslice函數(shù)具體實現(xiàn)同學有興趣的可以參考源碼src的 runtime/slice.go 。
當然,我們并不是每次都能在slice初始化的時候就能準確預(yù)估到最終的使用容量的。所以這里使用了一個“盡量”。明白是否設(shè)置slice容量的區(qū)別,我們在能預(yù)估容量的時候,請盡量使用方法2那種預(yù)估容量后的slice初始化方式。
問題:如果不設(shè)置cap,make slice的時候,創(chuàng)建的cap為多大?
如果不設(shè)置cap,不管是使用make,還是直接使用[]slice 進行初始化,編譯器都會計算初始化所需的空間,使用最小化的cap進行初始化。
a?:=?make([]int,?0)??//?cap?為0
a?:=?[]int{1,2,3}?//?cap?為3
可以從ssa看出
image-20211221095655104問題:slice什么時候決定擴張?
之前寫過一篇文章 https://www.cnblogs.com/yjf512/p/10714792.html 里面得出的結(jié)論就是slice在編譯期就決定是否要調(diào)用growslice。
這個邏輯是正確的。
編譯器在ssa的時候 對于append是會轉(zhuǎn)換為 OAPPEND(cmd/compile/internal/typecheck/universe.go) 。而在 cmd/compile/internal/ssagen/ssa.go 中,對其進行判斷。
image-20211221101614497目前還看不懂下面append下面的邏輯,不過基于這個注釋,能了解到這里growslice的邏輯。比較擴容前后大小,如果原先cap小于擴容后需要cap,就growslice。
總結(jié)
琢磨了四個關(guān)于切片的問題:
問題:go的切片數(shù)據(jù)結(jié)構(gòu)是什么樣子的?
問題:為什么在初始化slice的時候盡量補全cap?
問題:如果不設(shè)置cap,make slice的時候,創(chuàng)建的cap為多大?
問題:slice什么時候決定擴張?
感覺第四個問題還沒想透。
推薦閱讀
我為大家整理了一份從入門到進階的Go學習資料禮包,包含學習建議:入門看什么,進階看什么。關(guān)注公眾號 「polarisxu」,回復(fù)?ebook?獲??;還可以回復(fù)「進群」,和數(shù)萬 Gopher 交流學習。
