Go 基礎(chǔ)篇之切片Slice

今天開(kāi)工第一天凤薛,2023 xdm 什么目標(biāo),評(píng)論區(qū)聊聊晋柱。今天我們來(lái)聊聊切片(Slice)砸讳。

1. 說(shuō)在前面

在之前的 Go 語(yǔ)言基礎(chǔ)篇數(shù)組我們說(shuō)到數(shù)組的長(zhǎng)度是固定的琢融,申明了之后就不能改變其長(zhǎng)度。所以數(shù)組就會(huì)有很多的局限性簿寂。舉個(gè)例子:

image

Go 數(shù)組 s 我們此時(shí)已經(jīng)申明長(zhǎng)度為 5漾抬,我們不能在往數(shù)組 s 中添加新的元素了,在業(yè)務(wù)開(kāi)發(fā)中這樣的集合就不太適用陶耍。

2. 什么是切片

Go 語(yǔ)言中提供了一種靈活奋蔚、功能強(qiáng)悍的內(nèi)置類型切片(又稱動(dòng)態(tài)數(shù)組)Slice,和數(shù)組相比切片的長(zhǎng)度是不固定的,可以向其追加元素泊碑。

2.1 切片數(shù)據(jù)結(jié)構(gòu)

切片是一個(gè)引用類型坤按,底層源碼是用結(jié)構(gòu)體來(lái)表示的,其中:

image
  • Data 是指向數(shù)組的指針
  • Len 是當(dāng)前切片的長(zhǎng)度
  • Cap 是當(dāng)前切片的容量馒过,也是 Data 數(shù)組的大小

Data是一片連續(xù)的內(nèi)存空間臭脓,在這片空間中存儲(chǔ)著切片的所有元素,底層儲(chǔ)存上也是連續(xù)的腹忽。我們可以簡(jiǎn)單理解成:切片是一片連續(xù)的內(nèi)存空間加上長(zhǎng)度與容量的標(biāo)識(shí)来累。


image

從上圖可以發(fā)現(xiàn)切片其實(shí)就是數(shù)組的引用,我們可以在運(yùn)行期間修改其長(zhǎng)度與范圍窘奏,當(dāng)切片引用的數(shù)組長(zhǎng)度不足會(huì)觸發(fā)擴(kuò)容嘹锁,此時(shí)切片指向的數(shù)組會(huì)發(fā)生變化,但是對(duì)用戶來(lái)說(shuō)是無(wú)感知的着裹。

3. 切片

3.1 初始化

我們可以聲明一個(gè)未指定大小的數(shù)組來(lái)定義切片:

var identifier []type

其中:identifier 表示變量名领猾,type 表示切片中的元素類型,舉個(gè)例子

slice[0:10]
slice := []int{1, 2, 3, 4, 5, 6}
slice := make([]int, 10)

總結(jié)一下骇扇,大致有 3 種

  • 通過(guò)下標(biāo)的方式獲得數(shù)組或者切片的一部分摔竿;
  • 使用字面量初始化新的切片;
  • 使用關(guān)鍵字 make 創(chuàng)建切片少孝;

3.2 切片的長(zhǎng)度和容量

切片是可索引的继低,Go 語(yǔ)言中切片提供內(nèi)置的方法。

  • len() 方法獲取長(zhǎng)度稍走;
  • cap() 可以計(jì)算切片的容量袁翁;
image

以上會(huì)輸出以下結(jié)果為:

len=3 cap=5 slice=[0 0 0]

3.3 切片空值判斷

一個(gè)切片在未初始化之前默認(rèn)為 nil,默認(rèn)長(zhǎng)度為 0婿脸,所以判斷是否使用 len(s) == 0梦裂,不應(yīng)該使用 s == nil

3.4 切片遍歷

切片的遍歷方式和數(shù)組一樣盖淡,均支持索引遍歷和 for range 遍歷。

image

3.5 切片追加和擴(kuò)容

3.5.1 追加

日常開(kāi)發(fā)中凿歼,我們經(jīng)常會(huì)需要向一個(gè)切片中動(dòng)態(tài)追加元素褪迟。Go 語(yǔ)言內(nèi)置函數(shù) append()可以向切片中追加元素,append關(guān)鍵字支持一次添加一個(gè)或多個(gè)元素答憔,也支持追加一個(gè)切片的所有元素味赃。舉個(gè)例子:

image

其中,slice 無(wú)需初始化虐拓,在 append 中可以直接使用心俗。

注意append 操作會(huì)發(fā)生擴(kuò)容。

3.5.2 擴(kuò)容

Go 語(yǔ)言中切片底層指向的是一個(gè)數(shù)組的指針,當(dāng)這個(gè)數(shù)組的容量可以放的下當(dāng)前新增的元素城榛,則向其追加元素揪利,當(dāng)?shù)讓拥臄?shù)組容量不足以容下新增的元素,此時(shí)切片會(huì)按照一定的策略進(jìn)行 “擴(kuò)容”狠持,此時(shí)切片指向就會(huì)指向新的數(shù)組指針疟位。舉個(gè)例子:

package main

import "fmt"

func main() {
    var slice []int
    for i := 0; i < 6; i++ {
        slice = append(slice, i)
        fmt.Printf("%v  len:%d  cap:%d  ptr:%p\n", slice, len(slice), cap(slice), slice)
    }
}

以上會(huì)輸入以下結(jié)果:

[0]  len:1  cap:1  ptr:0xc000016060
[0 1]  len:2  cap:2  ptr:0xc000016090
[0 1 2]  len:3  cap:4  ptr:0xc000014060
[0 1 2 3]  len:4  cap:4  ptr:0xc000014060
[0 1 2 3 4]  len:5  cap:8  ptr:0xc000074040
[0 1 2 3 4 5]  len:6  cap:8  ptr:0xc000074040

從上面的結(jié)果可以看出:

  1. append 函數(shù)會(huì)將元素追加到切片的尾部。
  2. 切片 slice 的容量是按照1喘垂,2甜刻,4,8正勒,16 進(jìn)行自動(dòng)擴(kuò)容得院,每次擴(kuò)容后都是擴(kuò)容前的2倍。

擴(kuò)容策略如下:(感興趣的同學(xué)可以看下底層源碼)

  • 如果期望容量大于當(dāng)前容量的 2 倍就會(huì)使用期望容量章贞;
  • 如果當(dāng)前切片的長(zhǎng)度小于 1024 就會(huì)將容量翻倍祥绞;
  • 如果當(dāng)前切片的長(zhǎng)度大于 1024 就會(huì)每次增加 25% 的容量,直到新容量大于期望容量阱驾;

可以通過(guò)查看 $GOROOT/src/runtime/slice.go 源碼或 github 倉(cāng)庫(kù)(點(diǎn)擊跳轉(zhuǎn))就谜,其中擴(kuò)容核心代碼如下:

image

需要注意的是,切片擴(kuò)容還會(huì)根據(jù)切片中元素的類型不同而做不同的處理里覆,比如 intstring類型的處理方式就不一樣丧荐。

3.6 拷貝切片

切片的拷貝雖然不是常見(jiàn)的操作,但是卻是我們學(xué)習(xí)切片實(shí)現(xiàn)原理必須要涉及的喧枷。

Go 語(yǔ)言內(nèi)置 copy() 函數(shù)可以迅速地將一個(gè)切片的數(shù)據(jù)復(fù)制到另外一個(gè)切片空間中虹统,copy() 函數(shù)的使用格式如下:

copy(destSlice, srcSlice []T)

其中:

  • srcSlice: 數(shù)據(jù)來(lái)源切片
  • destSlice: 目標(biāo)切片

image

需要注意的是,整塊拷貝內(nèi)存仍然會(huì)占用非常多的資源隧甚,在大切片上執(zhí)行拷貝操作時(shí)一定要注意對(duì)性能的影響车荔。

4 總結(jié)

本文講了一下切片的日常使用以及需要注意的點(diǎn),切片還有很多比較深一點(diǎn)的知識(shí)點(diǎn)戚扳,后面有機(jī)會(huì)再出一篇吧忧便,需要注意的是在遇到大切片擴(kuò)容或者復(fù)制時(shí)可能會(huì)發(fā)生大規(guī)模的內(nèi)存拷貝,一定要減少類似操作避免影響程序的性能帽借。

歡迎點(diǎn)贊關(guān)注珠增,公眾號(hào)搜:程序員祝融

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市砍艾,隨后出現(xiàn)的幾起案子蒂教,更是在濱河造成了極大的恐慌,老刑警劉巖脆荷,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凝垛,死亡現(xiàn)場(chǎng)離奇詭異懊悯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)梦皮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門炭分,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人届氢,你說(shuō)我怎么就攤上這事欠窒。” “怎么了退子?”我有些...
    開(kāi)封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵岖妄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我寂祥,道長(zhǎng)荐虐,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任丸凭,我火速辦了婚禮福扬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惜犀。我一直安慰自己铛碑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布虽界。 她就那樣靜靜地躺著汽烦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪莉御。 梳的紋絲不亂的頭發(fā)上撇吞,一...
    開(kāi)封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音礁叔,去河邊找鬼牍颈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛琅关,可吹牛的內(nèi)容都是我干的煮岁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼涣易,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼人乓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起都毒,我...
    開(kāi)封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碰缔,沒(méi)想到半個(gè)月后账劲,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年瀑焦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腌且。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡榛瓮,死狀恐怖铺董,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情禀晓,我是刑警寧澤精续,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站粹懒,受9級(jí)特大地震影響重付,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凫乖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一确垫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧帽芽,春花似錦删掀、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至菊匿,卻和暖如春付呕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背跌捆。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工徽职, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人佩厚。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓姆钉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親抄瓦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子潮瓶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359