詳解Go語言中切片的長度與容量的區(qū)別
切片的聲明
切片可以看成是數(shù)組的引用(實(shí)際上切片的底層數(shù)據(jù)結(jié)構(gòu)確實(shí)是數(shù)組)。在 Go
中,每個數(shù)組的大小是固定的,不能隨意改變大小,切片可以為數(shù)組提供動態(tài)增長和縮小的需求,但其本身并不存儲任何數(shù)據(jù)。
// 數(shù)組的聲明 var a [5]int //只指定長度,元素初始化為默認(rèn)值0 var a [5]int{1,2,3,4,5} // 切片的聲明 // 方法1:直接初始化 var s []int //聲明一個長度和容量為 0 的 nil 切片 var s []int{1,2,3,4,5} // 同時創(chuàng)建一個長度為5的數(shù)組 // 方法2:用make()函數(shù)來創(chuàng)建切片 var s = make([]int, 0, 5) // 切分?jǐn)?shù)組:var 變量名 []變量類型 = arr[low, high],low和high為數(shù)組的索引。 // 記住規(guī)則為:左閉右開 var arr = [5]int{1,2,3,4,5} var slice []int = arr[1:4] // [2,3,4]
切片的長度和容量
切片的長度是它所包含的元素個數(shù)。切片的容量是從它的第一個元素到其底層數(shù)組元素末尾的個數(shù)。切片 s
的長度和容量可通過表達(dá)式 len(s)
和 cap(s)
來獲取。
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println(s, len(s), cap(s)) // output: [0 1 2 3 4 5 6 7 8 9] 10 10 s1 := s[0:5] fmt.Println(s1, len(s1), cap(s1)) // output: [0 1 2 3 4] 5 10 s2 := s[5:] fmt.Println(s2, len(s2), cap(s2)) // output: [5 6 7 8 9] 5 5
切片追加元素后長度和容量的變化
append 函數(shù)
Go
提供了內(nèi)建的 append
函數(shù),為切片追加新的元素。
func append(s []T, vs ...T) []T
append
的返回值是一個包含原切片所有元素加上新添加元素的切片。
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println(s, len(s), cap(s)) sResult := append(s, 11) fmt.Println(sResult, len(sResult), cap(sResult)) // output: // [0 1 2 3 4 5 6 7 8 9] 10 10 // [0 1 2 3 4 5 6 7 8 9 11] 11 20
這個時候,我們就可以發(fā)現(xiàn),當(dāng)我們 append
元素進(jìn)入切片時,原切片的長度以及容量都發(fā)生了變化,但是它們的變化為什么會這樣呢?
下面我們一起看看源碼是怎么實(shí)現(xiàn)的。
切片的源代碼學(xué)習(xí)
Go
中切片的數(shù)據(jù)結(jié)構(gòu)可以在源碼下的 src/runtime/slice.go
中查看。以下源代碼基于 go1.16.7
版本。
切片的結(jié)構(gòu)體
切片作為數(shù)組的引用,有三個屬性字段:指向數(shù)組的指針、長度和容量。
type slice struct { // 指向底層數(shù)組的指針 array unsafe.Pointer // slice 當(dāng)前元素個數(shù),即 len() 時返回的數(shù) len int // slice 的容量,即 cap() 時返回的數(shù) cap int }
切片的擴(kuò)容
slice
通過調(diào)用 append
函數(shù)來針對slice
進(jìn)行尾部追加元素,如果此時 slice
的 cap
值小于當(dāng)前 len
加上 append
中傳入值的數(shù)量,就會調(diào)用 runtime.growslice
函數(shù),進(jìn)行擴(kuò)容。
我們這里只放出基本的擴(kuò)容規(guī)則的代碼解析,如果對內(nèi)存對齊、數(shù)據(jù)拷貝等感興趣,可自行查看對應(yīng)的源碼。
基本擴(kuò)容規(guī)則
func growslice(et *_type, old slice, cap int) slice { newcap := old.cap doublecap := newcap + newcap // 如果新容量大于舊容量的兩倍,則直接按照新容量大小申請 if cap > doublecap { newcap = cap } else { // 如果原有長度小于1024,則新容量是舊容量的2倍 if old.len < 1024 { newcap = doublecap } else { // 按照原有容量的 1/4 增加,直到滿足新容量的需要 for 0 < newcap && newcap < cap { newcap += newcap / 4 } if newcap <= 0 { newcap = cap } } } }
從源碼來看,實(shí)際上可以整理出幾個規(guī)則:
當(dāng)原切片長度小于 1024 時,新的切片長度直接加上 append
元素的個數(shù),容量則會直接 *2
當(dāng)原切片長度大于等于 1024 時,新的切片長度直接加上 append
元素的個數(shù),容量則會增加 1/4
總結(jié)
切片是一個結(jié)構(gòu)體,保存著切片的容量,長度以及指向數(shù)組的指針(數(shù)組的地址)。
從源碼來看,當(dāng)一個切片進(jìn)行擴(kuò)容時,會進(jìn)行 growslice,這是一個花銷較大的操作,在日常開發(fā)中,如果能明確知道切片的長度或者容量時,我們需要在初始化的時候聲明,避免切片頻繁擴(kuò)容而帶來的花銷。
到此這篇關(guān)于詳解Go語言中切片的長度與容量的區(qū)別的文章就介紹到這了,更多相關(guān)Go語言 切片長度與容量內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang通用的grpc?http基礎(chǔ)開發(fā)框架使用快速入門
這篇文章主要為大家介紹了golang通用的grpc?http基礎(chǔ)開發(fā)框架使用快速入門詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09Go語言json編碼駝峰轉(zhuǎn)下劃線、下劃線轉(zhuǎn)駝峰的實(shí)現(xiàn)
這篇文章主要介紹了Go語言json編碼駝峰轉(zhuǎn)下劃線、下劃線轉(zhuǎn)駝峰的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06golang?gorm的關(guān)系關(guān)聯(lián)實(shí)現(xiàn)示例
這篇文章主要為大家介紹了golang?gorm的關(guān)系關(guān)聯(lián)實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04一文帶你了解Go語言fmt標(biāo)準(zhǔn)庫輸入函數(shù)的使用
這篇文章主要為大家詳細(xì)介紹了Go語言中?fmt?標(biāo)準(zhǔn)庫輸入函數(shù)的使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-01-01

Golang優(yōu)雅關(guān)閉channel的方法示例