Go 專(zhuān)欄|復(fù)合數(shù)據(jù)類(lèi)型:數(shù)組和切片 slice

原文鏈接: Go 專(zhuān)欄|復(fù)合數(shù)據(jù)類(lèi)型:數(shù)組和切片 slice

公司 Tony 老師這兩天請(qǐng)假,找來(lái)了他的好朋友 Kevin 頂班态蒂,這兩個(gè)人的風(fēng)格真是相差十萬(wàn)八千里。

Tony 性格緩慢抬纸,手法輕柔论笔。到底有多輕呢?洗頭發(fā)的時(shí)候我都懷疑他是不是怕把我頭發(fā)弄濕。

Kevin 則完全不同,嗓音洪亮窿锉,風(fēng)風(fēng)火火。說(shuō)是洗頭發(fā)窖维,但我感覺(jué)他就是在扇我腦袋榆综。眼前水花四濺,霧氣繚繞铸史,仿佛都能看見(jiàn)彩虹。

理發(fā)的小感受怯伊,夸張了點(diǎn)兒琳轿。

經(jīng)過(guò)上一篇的學(xué)習(xí),對(duì) Go 應(yīng)該已經(jīng)越來(lái)越有感覺(jué)了耿芹,今天來(lái)點(diǎn)更高級(jí)的內(nèi)容:復(fù)雜數(shù)據(jù)類(lèi)型崭篡。

本篇主要介紹數(shù)組和切片 slice,開(kāi)整~

本文所有代碼基于 go1.16.6 編寫(xiě)吧秕。

數(shù)組

數(shù)組有兩個(gè)特點(diǎn):

  1. 固定長(zhǎng)度
  2. 元素類(lèi)型相同

正是因?yàn)槠溟L(zhǎng)度固定琉闪,所以相比于切片,在開(kāi)發(fā)過(guò)程中用的是比較少的砸彬。但數(shù)組是切片的基礎(chǔ)颠毙,理解了數(shù)組,再學(xué)習(xí)切片就容易多了砂碉。

聲明和初始化

聲明一個(gè)長(zhǎng)度是 3蛀蜜,元素類(lèi)型是 int 的數(shù)組。通過(guò)索引來(lái)訪問(wèn)數(shù)組元素增蹭,索引從 0 到數(shù)組長(zhǎng)度減 1滴某,內(nèi)置函數(shù) len 可以獲取數(shù)組長(zhǎng)度。

var a [3]int
// 輸出數(shù)組第一個(gè)元素
fmt.Println(a[0]) // 0
// 輸出數(shù)組長(zhǎng)度
fmt.Println(len(a)) // 3

數(shù)組初始值為元素類(lèi)型零值滋迈,也可以用數(shù)組字面量初始化數(shù)組霎奢。

// 數(shù)組字面量初始化
var b [3]int = [3]int{1, 2, 3}
var c [3]int = [3]int{1, 2}
fmt.Println(b)    // [1 2 3]
fmt.Println(c[2]) // 0

如果沒(méi)有顯示指定數(shù)組長(zhǎng)度,而是用 ...饼灿,那么數(shù)組長(zhǎng)度由實(shí)際的元素?cái)?shù)量決定幕侠。

// 使用 ...
d := [...]int{1, 2, 3, 4, 5}
fmt.Printf("%T\n", d) // [5]int

還可以指定索引位置來(lái)初始化,如果沒(méi)有指定數(shù)組長(zhǎng)度赔退,則長(zhǎng)度由索引來(lái)決定橙依。

// 指定索引位置初始化
e := [4]int{5, 2: 10}
f := [...]int{2, 4: 6}
fmt.Println(e) // [5 0 10 0]
fmt.Println(f) // [2 0 0 0 6]

多維數(shù)組

多維數(shù)組的聲明和初始化同理证舟,這里以二維數(shù)組來(lái)舉例說(shuō)明,有一點(diǎn)需要注意窗骑,多維數(shù)組僅第一維允許使用 ...女责。

// 二維數(shù)組
var g [4][2]int
h := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 聲明并初始化外層數(shù)組中索引為 1 和 3 的元素
i := [4][2]int{1: {20, 21}, 3: {40, 41}}
// 聲明并初始化外層數(shù)組和內(nèi)層數(shù)組的單個(gè)元素
j := [...][2]int{1: {0: 20}, 3: {1: 41}}
fmt.Println(g, h, i, j)

使用數(shù)組

只要數(shù)組元素是可比較的,那么數(shù)組就是可比較的创译,而且數(shù)組長(zhǎng)度也是數(shù)組類(lèi)型的一部分抵知。

所以 [3]int[4]int 是兩種不同的類(lèi)型。

// 數(shù)組比較
a1 := [2]int{1, 2}
a2 := [...]int{1, 2}
a3 := [2]int{1, 3}
// a4 := [3]int{1, 2}
fmt.Println(a1 == a2, a1 == a3, a2 == a3) // true false false
// fmt.Println(a1 == a4)                     // invalid operation: a1 == a4 (mismatched types [2]int and [3]int)

數(shù)組遍歷:

// 數(shù)組遍歷
for i, n := range e {
    fmt.Println(i, n)
}

值類(lèi)型

Go 數(shù)組是值類(lèi)型软族,賦值和傳參都會(huì)復(fù)制整個(gè)數(shù)組刷喜。

從輸出結(jié)果可以看出來(lái),內(nèi)容都是相同的立砸,但地址不同掖疮。

package main

import "fmt"

func main() {
    // 數(shù)組復(fù)制
    x := [2]int{10, 20}
    y := x
    fmt.Printf("x: %p, %v\n", &x, x) // x: 0xc00012e020, [10 20]
    fmt.Printf("y: %p, %v\n", &y, y) // y: 0xc00012e030, [10 20]
    test(x)
}

func test(a [2]int) {
    fmt.Printf("a: %p, %v\n", &a, a) // a: 0xc00012e060, [10 20]
}

再來(lái)看看函數(shù)傳參的情況:

package main

import "fmt"

func main() {
    x := [2]int{10, 20}

    // 傳參
    modify(x)
    fmt.Println("main: ", x) // main:  [10 20]
}

func modify(a [2]int) {
    a[0] = 30
    fmt.Println("modify: ", a) // modify:  [30 20]
}

同樣從結(jié)果可以看到,modify 中數(shù)組內(nèi)容修改后颗祝,main 中數(shù)組內(nèi)容并沒(méi)有變化浊闪。

那么,有沒(méi)有可能在函數(shù)內(nèi)修改螺戳,而影響到函數(shù)外呢搁宾?答案是可以的,接下來(lái)要說(shuō)的切片就可以做到倔幼。

切片 slice

切片是一種引用類(lèi)型盖腿,它有三個(gè)屬性:指針,長(zhǎng)度和容量损同。

  1. 指針:指向 slice 可以訪問(wèn)到的第一個(gè)元素翩腐。
  2. 長(zhǎng)度:slice 中元素個(gè)數(shù)。
  3. 容量:slice 起始元素到底層數(shù)組最后一個(gè)元素間的元素個(gè)數(shù)揖庄。

看到這樣的解釋是不是一臉懵呢栗菜?別慌,咱們來(lái)詳細(xì)解釋一下蹄梢。

它的底層結(jié)構(gòu)是這樣的:

再來(lái)看一個(gè)例子疙筹,看看到底各部分都是什么意思。

底層是一個(gè)包含 10 個(gè)整型元素的數(shù)組禁炒,data1 指向數(shù)組第 4 個(gè)元素而咆,長(zhǎng)度是 3,容量取到數(shù)組最后一個(gè)元素幕袱,是 7暴备。data2 指向數(shù)組第 5 個(gè)元素,長(zhǎng)度是 4们豌,容量是 6涯捻。

創(chuàng)建切片

創(chuàng)建切片有兩種方式:

第一種方式是基于數(shù)組創(chuàng)建:

// 基于數(shù)組創(chuàng)建切片
var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8}

s1 := array[3:6]
s2 := array[:5]
s3 := array[4:]
s4 := array[:]

fmt.Printf("s1: %v\n", s1) // s1: [4 5 6]
fmt.Printf("s2: %v\n", s2) // s2: [1 2 3 4 5]
fmt.Printf("s3: %v\n", s3) // s3: [5 6 7 8]
fmt.Printf("s4: %v\n", s4) // s4: [1 2 3 4 5 6 7 8]

第二種方式是使用內(nèi)置函數(shù) make 來(lái)創(chuàng)建:

// 使用 make 創(chuàng)建切片
// len: 10, cap: 10
a := make([]int, 10)
// len: 10, cap: 15
b := make([]int, 10, 15)

fmt.Printf("a: %v, len: %d, cap: %d\n", a, len(a), cap(a))
fmt.Printf("b: %v, len: %d, cap: %d\n", b, len(b), cap(b))

使用切片

遍歷

和遍歷數(shù)組方法相同浅妆。

// 切片遍歷
for i, n := range s1 {
    fmt.Println(i, n)
}

比較

不能使用 == 來(lái)測(cè)試兩個(gè) slice 是否有相同元素,但 slice 可以和 nil 比障癌。slice
類(lèi)型的零值是 nil凌外,表示沒(méi)有對(duì)應(yīng)的底層數(shù)組,而且長(zhǎng)度和容量都是零涛浙。

但也要注意康辑,長(zhǎng)度和容量都是零的,其值也并不一定是 nil轿亮。

// 比較
var s []int
fmt.Println(len(s) == 0, s == nil) // true true
s = nil
fmt.Println(len(s) == 0, s == nil) // true true
s = []int(nil)
fmt.Println(len(s) == 0, s == nil) // true true
s = []int{}
fmt.Println(len(s) == 0, s == nil) // true false

所以疮薇,判斷 slice 是否為空,要用內(nèi)置函數(shù) len我注,而不是判斷其是否為 nil按咒。

追加元素

使用內(nèi)置函數(shù) append

// 追加
s5 := append(s4, 9)
fmt.Printf("s5: %v\n", s5) // s5: [1 2 3 4 5 6 7 8 9]
s6 := append(s4, 10, 11)
fmt.Printf("s6: %v\n", s6) // s5: [1 2 3 4 5 6 7 8 10 11]

追加另一個(gè)切片仓手,需要在另一個(gè)切片后面跟三個(gè)點(diǎn)胖齐。

// 追加另一個(gè)切片
s7 := []int{12, 13}
s7 = append(s7, s6...)
fmt.Printf("s7: %v\n", s7) // s7: [12 13 1 2 3 4 5 6 7 8 10 11]

復(fù)制

使用內(nèi)置函數(shù) copy

// 復(fù)制
s8 := []int{1, 2, 3, 4, 5}
s9 := []int{5, 4, 3}
s10 := []int{6}

copy(s8, s9)
fmt.Printf("s8: %v\n", s8) // s8: [5 4 3 4 5]
copy(s10, s9)
fmt.Printf("s10: %v\n", s10) // s10: [5]

引用類(lèi)型

上文介紹數(shù)組時(shí)說(shuō)過(guò)嗽冒,數(shù)組屬于值類(lèi)型,所以在傳參時(shí)會(huì)復(fù)制整個(gè)數(shù)組內(nèi)容补履,如果數(shù)組很大的話添坊,是很影響性能的。而傳遞切片只會(huì)復(fù)制切片本身箫锤,并不影響底層數(shù)組贬蛙,是很高效的。

package main

import "fmt"

func main() {
    s9 := []int{5, 4, 3}

    // 傳參
    modify(s9)
    fmt.Println("main: ", s9) // main:  [30 4 3]
}

func modify(a []int) {
    a[0] = 30
    fmt.Println("modify: ", a) // modify:  [30 4 3]
}

modify 中修改的值會(huì)影響到 main 中谚攒。

總結(jié)

本文學(xué)習(xí)了復(fù)合數(shù)據(jù)類(lèi)型的前兩種:數(shù)組和切片阳准。分別介紹了它們的創(chuàng)建,常用操作馏臭,以及函數(shù)間的傳遞野蝇。

數(shù)組長(zhǎng)度固定,是切片的基礎(chǔ)括儒;切片長(zhǎng)度可變绕沈,多一個(gè)容量屬性,其指針指向的底層結(jié)構(gòu)就是數(shù)組帮寻。

在函數(shù)傳參過(guò)程中乍狐,數(shù)組如果很大的話,很影響效率固逗,而切片則解決了這個(gè)問(wèn)題浅蚪,效率更高藕帜。

在日常開(kāi)發(fā)中,使用切片的頻率會(huì)更高一些惜傲。


文章中的腦圖和源碼都上傳到了 GitHub洽故,有需要的同學(xué)可自行下載。

地址: https://github.com/yongxinz/gopher/tree/main/sc

Go 專(zhuān)欄文章列表:

  1. 開(kāi)發(fā)環(huán)境搭建以及開(kāi)發(fā)工具 VS Code 配置

  2. 變量和常量的聲明與賦值

  3. 基礎(chǔ)數(shù)據(jù)類(lèi)型:整數(shù)操漠、浮點(diǎn)數(shù)收津、復(fù)數(shù)、布爾值和字符串

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浊伙,一起剝皮案震驚了整個(gè)濱河市撞秋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嚣鄙,老刑警劉巖吻贿,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異哑子,居然都是意外死亡舅列,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)卧蜓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)帐要,“玉大人,你說(shuō)我怎么就攤上這事弥奸≌セ荩” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵盛霎,是天一觀的道長(zhǎng)赠橙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)愤炸,這世上最難降的妖魔是什么期揪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮规个,結(jié)果婚禮上凤薛,老公的妹妹穿的比我還像新娘。我一直安慰自己绰姻,他們只是感情好枉侧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著狂芋,像睡著了一般榨馁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帜矾,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天翼虫,我揣著相機(jī)與錄音屑柔,去河邊找鬼。 笑死珍剑,一個(gè)胖子當(dāng)著我的面吹牛掸宛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播招拙,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼唧瘾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了别凤?” 一聲冷哼從身側(cè)響起饰序,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎规哪,沒(méi)想到半個(gè)月后求豫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诉稍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年蝠嘉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片杯巨。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚤告,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出服爷,到底是詐尸還是另有隱情罩缴,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布层扶,位于F島的核電站,受9級(jí)特大地震影響烙荷,放射性物質(zhì)發(fā)生泄漏镜会。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一终抽、第九天 我趴在偏房一處隱蔽的房頂上張望戳表。 院中可真熱鬧,春花似錦昼伴、人聲如沸匾旭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)价涝。三九已至,卻和暖如春持舆,著一層夾襖步出監(jiān)牢的瞬間色瘩,已是汗流浹背伪窖。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留居兆,地道東北人覆山。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泥栖,于是被迫代替她去往敵國(guó)和親簇宽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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