Go語(yǔ)言的切片擴(kuò)容
一、切片
首先我們要知道切片的本質(zhì)是什么?
Slice的底層數(shù)據(jù)結(jié)構(gòu)(src/runtime/slice.go):
type slice struct {
array unsafe.Pointer // 指向底層數(shù)組的指針
len int // 當(dāng)前切片長(zhǎng)度
cap int // 底層數(shù)組容量
}切片頭(slice header)
+---------------------------+
| array -> 底層數(shù)組 |
| len:當(dāng)前長(zhǎng)度 |
| cap:最大容量 |
+---------------------------+
|
v
底層數(shù)組(underlying array)
+------+------+------+------+------+
| 1 | 2 | 3 | ? | ? |
+------+------+------+------+------+
^ ^
| |
起始元素 容量上限我們可以把切片比作一本相冊(cè)只有3個(gè)照片的位置 //這里的“位置”可以代指為數(shù)組的容量(cap :3)
而你手上有兩張照片去放到這個(gè)相冊(cè)里面 //這兩張照片就是長(zhǎng)度(len:2)
最后底層數(shù)據(jù)就是相片冊(cè)類型的結(jié)構(gòu) //array就是一個(gè)指向這個(gè)相冊(cè)的指針
二、擴(kuò)容觸發(fā)條件
在我們理解了切片是什么之后,就聊一下切片的擴(kuò)容觸發(fā):
nums := make([]int{0,3})// 創(chuàng)建一個(gè)切片,len=0, cap=3
num = append(nums, 1) //len=1, cap=3
num = append(nums, 2) //len=2, cap=3
num = append(nums, 3) //len=3, cap=3
num = append(nums, 4) //len=4, len > cap 觸發(fā)擴(kuò)容
當(dāng)執(zhí)行 append操作時(shí):
- 如果當(dāng)前容量(cap)足夠:直接在原底層數(shù)組末尾追加元素,修改
len并返回原切片(無(wú)新內(nèi)存分配) - 如果容量不足:觸發(fā)擴(kuò)容邏輯,分配新的更大的底層數(shù)組,拷貝舊數(shù)據(jù),再追加新元素
個(gè)人見(jiàn)解: 其實(shí)可以把這個(gè)切片當(dāng)成最多放3張照片的相片冊(cè)。當(dāng)我們想要放入第四張照片時(shí),
我們的想法肯定是買一個(gè)新的相片冊(cè)能放更多的照片。
至于買多大的相冊(cè)(怎么擴(kuò)容),Go官方給出了擴(kuò)容策略,請(qǐng)往下看。
三、切片擴(kuò)容機(jī)制
v1.18前的擴(kuò)容策略(了解)
在 Go 語(yǔ)言 v1.18 版本之前,當(dāng)執(zhí)行append向 slice 底層數(shù)組追加數(shù)據(jù)。若舊容量(oldCap)小于所需的最小容量(即cap < len+num)時(shí),會(huì)觸發(fā)擴(kuò)容操作,其擴(kuò)容規(guī)則:
| 舊容量范圍 | 新容量計(jì)算規(guī)則 |
|---|---|
| cap < 1024 | 新容量 = 舊容量 × 2(按2倍擴(kuò)容) |
| cap ≥ 1024 | 新容量 = 舊容量 × 1.25(按1.25 倍擴(kuò)容) |
v1.18+擴(kuò)容策略(需要掌握?。?!)
用append向slice底層數(shù)組追加num個(gè)數(shù)據(jù)。當(dāng)cap>= len+num時(shí),直接在slice底層對(duì)應(yīng)的數(shù)組進(jìn)行操作。如果cap< len+num需要擴(kuò)容:
若 newLen > 2 * oldCap,直接將容量擴(kuò)容至 newLen。(newlen = len + num)
若 oldCap < 256,將容量擴(kuò)容至 2 * oldCap。
若 oldCap >= 256,每次擴(kuò)容為 oldcap + (oldCap + 3 * 256)/4,重復(fù)此操作直到 newcap >= newLen。
若擴(kuò)容后容量溢出,則返回 newLen;否則返回計(jì)算后的 newcap。溢出會(huì)在后續(xù)邏輯觸發(fā) panic,不在此函數(shù)處理。
這里舉個(gè)例子:
a := make([]int,300,300) a = append(a,12) fmt.Println(len(a),cap(a))
輸出結(jié)果是什么?
輸
出
結(jié)
果
>>301 567//len(a),cap(a)
你做對(duì)了嗎?按照v1.18+的擴(kuò)容機(jī)制 ,300(oldcap) > 256, 所以newcap = 300 + (300 + 3 * 256)/4 = 567。
有的人可能會(huì)說(shuō),切片擴(kuò)容不是按照那個(gè)1024的閾值去判斷擴(kuò)容2倍/1.25倍嗎?其實(shí)說(shuō)的也是對(duì)的,只是依賴版本不同,切片的擴(kuò)容規(guī)則也就不一樣了。
但本質(zhì)上版本切片擴(kuò)容的邏輯就是減少擴(kuò)容次數(shù),避免浪費(fèi)內(nèi)存空間。
四、總結(jié)
- 理解切片的本質(zhì)
- 清楚切片擴(kuò)容的觸發(fā)條件:len + 溢出數(shù) > cap
- 根據(jù)切片擴(kuò)容機(jī)制,進(jìn)行擴(kuò)容:
Gov1.18前:以 1024 為閾值:cap<1024 時(shí)擴(kuò)容 2 倍,cap≥1024 時(shí)擴(kuò)容 1.25 倍。
到此這篇關(guān)于Go語(yǔ)言的切片擴(kuò)容的文章就介紹到這了,更多相關(guān)Go 切片擴(kuò)容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Go中攔截HTTP流數(shù)據(jù)時(shí)字段丟失的問(wèn)題
在開(kāi)發(fā)高并發(fā)的Web應(yīng)用時(shí),尤其是在處理HTTP代理和流數(shù)據(jù)攔截的場(chǎng)景下,遇到數(shù)據(jù)丟失的問(wèn)題并不罕見(jiàn),最近,在一個(gè)項(xiàng)目中,我遇到了一個(gè)棘手的問(wèn)題:在攔截并轉(zhuǎn)發(fā)HTTP流數(shù)據(jù)的過(guò)程中,某些數(shù)據(jù)字段因?yàn)樘幚磉^(guò)快而被丟失,所以本文給大家介紹如何解決這個(gè)問(wèn)題2024-08-08
Go語(yǔ)言指針訪問(wèn)結(jié)構(gòu)體的方法
這篇文章主要介紹了Go語(yǔ)言指針訪問(wèn)結(jié)構(gòu)體的方法,涉及Go語(yǔ)言指針及結(jié)構(gòu)體的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02

