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

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