go語言學習(5)--數組與切片

數組的聲明

func array() {
    var arr1 [5]int
    arr2 := [3]int{1, 3, 5}
    // 自行推斷數組長度
    arr3 := [...]int{1, 3, 5, 7, 9}
    // 二維數組
    var grid [4][5]int
    fmt.Println(arr1, arr2, arr3, grid)
    // [0 0 0 0 0] [1 3 5] [1 3 5 7 9] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
}

變量的聲明已經講過啦,不熟悉的可以看第二章

數組的遍歷

func arrRange() {
    arr1 := [...]int{2, 4, 6, 8, 10}
    for i := 0; i < len(arr1); i++ {
        fmt.Println(arr1[i])
    }

    for _, value := range arr1 {
        fmt.Println(value)
    }

兩種方法,一種傳統的下標遍歷一種上一章講到的range
推薦遍歷用 range, 很方便
在idea里,傳統的 for 循環智能提示是 for,forr就可以自動補全 range

值傳遞 or 引用傳遞

上一章講到這個概念了,這一章繼續舉個??

func printArray(arr [5]int) {
    arr[0] = 100
    for _, v := range arr {
        println(v)
    }
}

func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}
    arr2 := [3]int{1, 2, 3}
    printArray(arr1)
    printArray(arr2)
}

這個結果是什么呢

# command-line-arguments
lesson4(array)/array.go:45:12: cannot use arr2 (type [3]int) as type [5]int in argument to printArray

說的是類型不匹配,我需要一個[5]int的,你給我傳的是[3]int的,當然報錯啦

func printArray(arr [5]int) {
    arr[0] = 100
    for _, v := range arr {
        println(v)
    }
}
func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}
    printArray(arr1)
    fmt.Println(arr1)
}

打印的結果是什么?
當然還是[1,2,3,4,5]啦,因為你是把 arr1拷貝了一份,函數體內的變化并不影響外部的變量
如果想改變 arr[0]的值你得這樣

func printArray2(arr *[5]int) {
    arr[0] = 100
    for _, v := range arr {
        println(v)
    }
}

func main() {
    arr1 := [5]int{1, 2, 3, 4, 5}
    printArray(arr1)
    printArray2(&arr1)
    fmt.Println(arr1)
}

傳一個指向 arr1的指針,懂了嗎,在go里只有值傳遞,而且 array 這種類型也是值,而在其他語言里比如python,javascript array 都是引用類型
這幾段代碼可能讓你覺得go里的數組很難用,其實不然,下面我們講下數組的切片

切片slice

func main() {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
    fmt.Println("arr[2:6] = ", arr[2:6])
    fmt.Println("arr[2:6] = ", arr[:6])
    fmt.Println("arr[2:6] = ", arr[2:])
    fmt.Println("arr[2:6] = ", arr[:])
}

有編程基礎的應該都能知道,結果是:

arr[2:6] =  [2 3 4 5]
arr[2:6] =  [0 1 2 3 4 5]
arr[2:6] =  [2 3 4 5 6 7]
arr[2:6] =  [0 1 2 3 4 5 6 7]

再來看看切片

func updateSlice(s []int) {
    s[0] = 100
}

func main() {
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
    s1 := arr[2:]
    s2 := arr[:]
    fmt.Println(s2) // [0 1 2 3 4 5 6 7]
    fmt.Println("After updateSlice")
    updateSlice(s1)
    fmt.Println(s1) // [100 3 4 5 6 7]
    fmt.Println(arr) // [0 1 100 3 4 5 6 7]
}

Slice 本身沒有數據,只是對底層 array 的一個 view
鞏固一下

// s2 = [0 1 100 3 4 5 6 7]
fmt.Println("reSlice")
s2 = s2[:5]
fmt.Println(s2) // [0 1 100 3 4]
s2 = s2[2:]
fmt.Println(s2) // [100 3 4]

這些都理解,下面再看個例子

arr = [...]int{0,1,2,3,4,5,6,7}
s1 = arr[2:6]
s2 = s1[3:6]
fmt.Println("s1 = ",s1)
fmt.Println("s2 = ",s2)

這是不是有問題呢? s1是 arr 的第3個到第6個元素,總共4個
s2是不是下標越界了呢
運行一下

s1 =  [2 3 4 5]
s2 =  [5 6 7]

再來看看
fmt.Println(s1[4])
panic: runtime error: index out of range
很奇怪對不對,奇怪就對了
記住這個概念
Slice 本身沒有數據,只是對底層 array 的一個 view
來看個圖

image

  • s1為[2 3 4 5],s2為[5 6 7]
  • slice 可以向后擴展,不可以向前擴展
  • s[i]不可以超越len(s),向后擴展不可以超越cap(s)
    這就是cap capacity(容量)的概念
fmt.Printf("s1=%v,len(s1)=%d,cap(s1)=%d\n",
        s1, len(s1), cap(s1))
fmt.Printf("s2=%v,len(s2)=%d,cap(s2)=%d\n",
        s2, len(s2), cap(s2))

// s1=[2 3 4 5],len(s1)=4,cap(s1)=6
// s2=[5 6 7],len(s2)=3,cap(s2)=3

go的數組可以指定起始,結束和 capacity,而python是指定起始,結束和步長,不要混為一談
s1[1:2:3]指定 cap 為3
slice起初我也覺得有點繞,多練習就好了

append

arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}
s1 := arr[2:6]
s2 := s1[3:5]
s3 := append(s2, 10)
s4 := append(s3, 11)
s5 := append(s4, 12)
fmt.Println(s1, s2, s3, s4, s5)

// [2 3 4 5] [5 6] [5 6 10] [5 6 10 11] [5 6 10 11 12]

append和其他語言一樣,可以突破capacity的限制,依次在數組后面添加元素

擴容的規律

func printSlice(s []int) {
    fmt.Printf("len=%d, cap=%d\n", len(s), cap(s))
}

var s []int
for i := 0; i < 100; i++ {
    printSlice(s)
    s = append(s, 2*i+1)
}

貼幾個結果感受下

len=14, cap=16
len=15, cap=16
len=16, cap=16
...
len=31, cap=32
len=32, cap=32
...
len=63, cap=64
len=64, cap=64
len=65, cap=128
len=66, cap=128

java不同,java默認負載因子是0.75,go看的出來是capacity滿了才擴容,每次擴容兩倍,所以和java一樣,數組最好知道容量,上來就建好

還有一種創建數組的方法make

s2 = make([]int, 16)
s3 = make([]int, 10, 32)
printSlice(s2)
printSlice(s3)

// len=16, cap=16
// len=10, cap=32

后面會經常用到,創建數組和channel都很常用
copy(s2, s1)

func copy(dst, src []Type) int
fmt.Println(s2)

// [2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0]

刪除數組元素

fmt.Println("Deleting elements from slice")
s2 = append(s2[:3], s2[4:]...)
printSlice(s2)

fmt.Println("Poping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println(front)
printSlice(s2)

fmt.Println("Poping from tail")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(tail)
printSlice(s2)

/**
Deleting elements from slice
len=15, cap=16
Poping from front
2
len=14, cap=15
Poping from tail
0
len=13, cap=15
*/

總結

  • go里只有值傳遞
  • 不要直接用 array, 要用切片來操作
  • 注意cap 這個隱含的值,你如果不想別人能訪問到切片以外的數據,可以加上 cap 比如s[1:2:3]
  • 切片只是對底層 arrayview

上述代碼均已上傳至 github, 歡迎 star
https://github.com/yejunyu/golearn


image
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,663評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,125評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,506評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,614評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,402評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,934評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,021評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,168評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,690評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,596評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,288評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,027評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,404評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,662評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,398評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,743評論 2 370

推薦閱讀更多精彩內容

  • 切片 切片定義: 切片(slice)是對數組一個連續片段的引用,所以...
    liuxuech閱讀 832評論 0 0
  • 1.安裝 https://studygolang.com/dl 2.使用vscode編輯器安裝go插件 3.go語...
    go含羞草閱讀 1,562評論 0 6
  • 01.{ 換行: Opening Brace Can't Be Placed on a Separate Lin...
    碼農不器閱讀 2,416評論 0 14
  • 夜聽窗外有雨聲 雨中響起歌一曲 細聞原是朋友歌 嘹亮歌喉伴情深 雨夜那日有你我 日后回顧已足矣
    a2984b6bf0a1閱讀 173評論 3 2
  • 來北京蘇家坨很多次了,這里的早市比較有名,吃的、穿的、用的,早市上銷售的應有盡有。 所謂早市,就是只有半天時間的市...
    楓紅云天閱讀 881評論 11 15