go 數(shù)據(jù)結(jié)構(gòu) -- array&slice

數(shù)據(jù)類型的本質(zhì):固定內(nèi)存大小的別名。

數(shù)據(jù)類型的作用:編譯器預(yù)算對象或變量分配內(nèi)存空間的大小。

數(shù)組 array

數(shù)組是同一種數(shù)據(jù)類型的固定長度的序列虎忌,指向一段連續(xù)的內(nèi)存空間。

聲明

arr := [6]int{1,2,3,4,5,6}

特性

  • 因為數(shù)組的內(nèi)存分布是連續(xù)的,所以數(shù)組訪問任一元素的效率很高宙址,O(1)。
  • 元素類型和數(shù)組長度都是數(shù)組類型的一部分调卑,不同長度的數(shù)組是不同的類型抡砂。
  • 數(shù)組是值類型,改變其副本的值不會改變原數(shù)組的值恬涧。

數(shù)組指針和指針數(shù)組

數(shù)組指針

即聲明一個指針變量注益,指向一個數(shù)組:

arr := [6]int{5:9}  //第6個元素為9
var ptr *[6]int = &arr

需要注意的是,指針變量ptr的類型是*[6]int溯捆,也就是說它只能指向包含6個元素的整型數(shù)組丑搔,否則編譯報錯厦瓢。

指針數(shù)組

即數(shù)組的元素類型是指針。

x,y := 1,2
var arrPtr = [5]*int{1:&x,3:&y}

函數(shù)間傳遞數(shù)組

如果實參是一個非常大的數(shù)組啤月,因函數(shù)參數(shù)只有值傳遞煮仇,即需要重新拷貝變量,拷貝導(dǎo)致的內(nèi)存和性能開銷較大谎仲≌愕妫可將數(shù)組指針作為參數(shù)傳遞,拷貝開銷小郑诺,但也需要考慮到函數(shù)內(nèi)數(shù)組指針修改會影響原數(shù)組夹姥。

切片 slice

切片與數(shù)組類似,存放相同數(shù)據(jù)類型的元素辙诞,不同的是切片基于底層數(shù)組辙售,可按需擴(kuò)縮容。切片是底層數(shù)組的一個視圖飞涂,也可以說切片是對數(shù)組的抽象旦部。

切片的內(nèi)部實現(xiàn)中有三個變量,指針 ptr封拧,長度 len 和容量 cap志鹃。

// runtime/slice.go
type slice struct {
    array unsafe.Pointer // 指針,數(shù)據(jù)存儲在底層數(shù)組中泽西,而指針指向可以通過切片訪問到的第一個元素曹铃。
    len   int            // 長度,我們只能訪問切片長度范圍內(nèi)的元素捧杉。
    cap   int            // 容量陕见,表示可以擴(kuò)展的最大大小。容量必須大于等于長度味抖。
}
圖片

應(yīng)注意底層數(shù)組是可以被多個 slice 同時指向的评甜,因此對一個 slice 的元素進(jìn)行操作是有可能影響到其他 slice 的。

聲明與初始化

make 函數(shù)創(chuàng)建

s1 := make([]int,3,5)   // 創(chuàng)建一個可用的空切片仔涩,長度為3忍坷,容量為5,容量也可以不傳
fmt.Println(s1)         // [0 0 0]

字面量創(chuàng)建

s2 := []int{1,2,3,4,5}  // 創(chuàng)建長度和容量都是5的整型切片

截取已有的數(shù)組或者切片創(chuàng)建

a := []int{1,2,3,4,5}
t := a[3:4]

截取得到的切片熔脂,和原切片或原數(shù)組共享底層數(shù)組佩研,但是兩者能訪問到底層數(shù)組的范圍是不同的。

截取獲得的新切片的長度和容量計算

對容量為 k 的切片執(zhí)行[i,j]操作之后霞揉,獲得的新切片的長度和容量是j-ik-i旬薯。

對容量為 k 的切片執(zhí)行[i,j,l]操作之后,獲得的新切片的長度和容量是j-il-i适秩,其中第三個變量 l 用于限定新切片的容量绊序,j 和 l 必須在原數(shù)組或者原 slice 的容量范圍內(nèi)(小于等于 k)硕舆。

新切片與原數(shù)組或者切片的關(guān)系

截取得到的新切片和原數(shù)組或原切片是基于同一個底層數(shù)組的,所以當(dāng)修改的時候骤公,底層數(shù)組的值就會被改變抚官,原切片的值也隨之改變了。

可以把截取得到的新切片看做是原數(shù)組或原切片的一個視圖淋样。

圖片

但如果因為執(zhí)行 append 操作使得新 slice 底層數(shù)組擴(kuò)容耗式,移動到了新的位置胁住,兩者就不會相互影響了趁猴。所以,問題的關(guān)鍵在于兩者是否會共用底層數(shù)組彪见。

nil 切片和空切片

nil 切片

var s []int                 // 聲明了一個 nil 切片
fmt.Println(s == nil)       // 輸出 true
fmt.Println(len(s), cap(s)) // 輸出:0 0
s = append(s, 1)            // 使用 append 函數(shù)可以為 nil 切片增加元素

切片的零值是 nil儡司。因為切片是底層數(shù)組的引用,nil 切片指向底層數(shù)組的指針為 nil余指,即不指向任何底層數(shù)組捕犬。

空切片

s := make([]int, 0)         // 1、使用 make 創(chuàng)建空的整型切片
s := []int{}                // 2酵镜、使用切片字面量創(chuàng)建空的整型切片
fmt.Println(s == nil)       // 輸出 false
fmt.Println(s)              // 輸出:[]
fmt.Println(len(s), cap(s)) // 輸出:0 0

與 nil 切片一樣碉碉,空切片的長度和容量也都是 0,說明切片底層的數(shù)組大小為 0淮韭,是一個空數(shù)組(沒有分配任何的存儲空間)垢粮。

不管是使用 nil 切片還是空切片,對其調(diào)用內(nèi)置函數(shù) append靠粪、len 和 cap 的效果都是一樣的蜡吧。官方建議盡量使用 nil 切片。

nil slice 可以直接 append 占键,因為 append 最終都是調(diào)用 mallocgc 來向 Go 的內(nèi)存管理器申請到一塊內(nèi)存昔善,然后再賦給原來的 nil slice 或 empty slice。

增長/擴(kuò)容

使用內(nèi)建函數(shù) append 能夠幫我們處理切片增長的一些細(xì)節(jié)畔乙。

  • append 可往切片追加一個或多個值君仆,然后返回一個新的切片。應(yīng)注意 Go 編譯器不允許調(diào)用了 append 函數(shù)后不使用返回值牲距。
  • append 函數(shù)會使新的切片長度增加返咱。
  • append 函數(shù)實際上是往底層數(shù)組添加元素。
  • 新切片容量是否增長取決于原切片剩余容量和需要追加的元素數(shù)目嗅虏。當(dāng)剩余容量不足時洛姑,append 函數(shù)會創(chuàng)建一個新的數(shù)組并將原數(shù)組元素拷貝到新數(shù)組中,再追加新的值皮服。append 函數(shù)會智能地增加底層數(shù)組的容量楞艾,目前的算法是:當(dāng)數(shù)組容量小于等于1024時参咙,會成倍地增加;當(dāng)超過1024硫眯,增長因子變?yōu)?.25蕴侧,也就是說每次會增加25%的容量(這個說法是錯誤的,還有內(nèi)存對齊的操作)两入。

要注意的是净宵,通過截取創(chuàng)建的切片,如果切片剩余容量能存下 append 追加的元素裹纳,切片長度增長而不擴(kuò)容择葡,追加的元素會改變原切片或數(shù)組的值;如果不能存下追加的元素剃氧,切片長度增長并進(jìn)行擴(kuò)容敏储,擴(kuò)容操作為 append 函數(shù)創(chuàng)建一個新的底層數(shù)組,將原數(shù)組的值復(fù)制到新數(shù)組里朋鞍,再追加新的值已添,因為新切片與原切片或數(shù)組的底層數(shù)組不再相同,追加的元素不會改變原切片或數(shù)組的值滥酥,且后續(xù)兩個切片不再相互影響更舞。關(guān)鍵在于兩者是否會共用底層數(shù)組

一般我們在創(chuàng)建新切片的時候坎吻,最好要讓新切片的長度和容量一樣缆蝉,這樣我們在追加操作的時候就會生成新的底層數(shù)組,和原有數(shù)組分離禾怠,就不會因為共用底層數(shù)組而引起奇怪問題返奉。

copy 函數(shù)

Go 提供了內(nèi)置函數(shù) copy,可以將一個切片復(fù)制到另一個切片吗氏。

func copy(dst, src []Type) int  // 函數(shù)返回兩者長度的最小值

如果 dst 切片為 nil 切片芽偏,copy 之后,dst 切片仍為 nil弦讽。而 nil 切片 append 非空切片后污尉,變?yōu)榉强涨衅?/p>

copy 只會復(fù)制,不會追加往产。

函數(shù)間傳遞切片

切片在函數(shù)間以值的方式傳遞被碗。當(dāng)直接用切片作為函數(shù)參數(shù)時,可以改變切片的元素仿村,不能改變切片本身锐朴;想要改變切片本身,可以將改變后的切片返回蔼囊,函數(shù)調(diào)用者接收改變后的切片或者將切片指針作為函數(shù)參數(shù)焚志。

由于切片的尺寸很幸旅浴(在 64 位架構(gòu)的機(jī)器上,一個切片需要 24 字節(jié)的內(nèi)存:指針字段酱酬、長度和容量字段各需要 8 字節(jié))壶谒,在函數(shù)間復(fù)制和傳遞切片成本很低。

刪除切片中的元素

Go 沒有提供刪除切片元素的函數(shù)膳沽,可以通過截取和 append 函數(shù)實現(xiàn)汗菜。

s := []int{1, 2, 3, 4, 5, 6}
s = append(s[:2], s[3:]...)    // 刪除索引為2的元素

其他騷操作

切片垃圾回收

巨型 slice 產(chǎn)生的垃圾回收問題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市挑社,隨后出現(xiàn)的幾起案子陨界,更是在濱河造成了極大的恐慌,老刑警劉巖滔灶,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件普碎,死亡現(xiàn)場離奇詭異吼肥,居然都是意外死亡录平,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門缀皱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斗这,“玉大人,你說我怎么就攤上這事啤斗”砑” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵钮莲,是天一觀的道長免钻。 經(jīng)常有香客問我,道長崔拥,這世上最難降的妖魔是什么极舔? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮链瓦,結(jié)果婚禮上拆魏,老公的妹妹穿的比我還像新娘。我一直安慰自己慈俯,他們只是感情好渤刃,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贴膘,像睡著了一般卖子。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刑峡,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天洋闽,我揣著相機(jī)與錄音璃哟,去河邊找鬼。 笑死喊递,一個胖子當(dāng)著我的面吹牛随闪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播骚勘,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼铐伴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了俏讹?” 一聲冷哼從身側(cè)響起当宴,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泽疆,沒想到半個月后户矢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡殉疼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年梯浪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓢娜。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡挂洛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出眠砾,到底是詐尸還是另有隱情虏劲,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布褒颈,位于F島的核電站柒巫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谷丸。R本人自食惡果不足惜堡掏,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望淤井。 院中可真熱鬧布疼,春花似錦、人聲如沸币狠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漩绵。三九已至贱案,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宝踪。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工侨糟, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘩燥。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓秕重,卻偏偏與公主長得像,于是被迫代替她去往敵國和親厉膀。 傳聞我的和親對象是個殘疾皇子溶耘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容