<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 專欄|復(fù)合數(shù)據(jù)類型:數(shù)組和切片 slice

          共 7668字,需瀏覽 16分鐘

           ·

          2021-08-03 05:43

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

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

          Kevin 則完全不同,嗓音洪亮,風(fēng)風(fēng)火火。說是洗頭發(fā),但我感覺他就是在扇我腦袋。眼前水花四濺,霧氣繚繞,仿佛都能看見彩虹。

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

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

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

          數(shù)組

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

          1. 固定長度

          2. 元素類型相同

          正是因?yàn)槠溟L度固定,所以相比于切片,在開發(fā)過程中用的是比較少的。但數(shù)組是切片的基礎(chǔ),理解了數(shù)組,再學(xué)習(xí)切片就容易多了。

          聲明和初始化

          聲明一個(gè)長度是 3,元素類型是 int 的數(shù)組。通過索引來訪問數(shù)組元素,索引從 0 到數(shù)組長度減 1,內(nèi)置函數(shù) len 可以獲取數(shù)組長度。

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

          數(shù)組初始值為元素類型零值,也可以用數(shù)組字面量初始化數(shù)組。

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

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

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

          還可以指定索引位置來初始化,如果沒有指定數(shù)組長度,則長度由索引來決定。

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

          多維數(shù)組

          多維數(shù)組的聲明和初始化同理,這里以二維數(shù)組來舉例說明,有一點(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: {2021}, 3: {4041}}
          // 聲明并初始化外層數(shù)組和內(nèi)層數(shù)組的單個(gè)元素
          j := [...][2]int{1: {020}, 3: {141}}
          fmt.Println(g, h, i, j)

          使用數(shù)組

          只要數(shù)組元素是可比較的,那么數(shù)組就是可比較的,而且數(shù)組長度也是數(shù)組類型的一部分。

          所以 [3]int[4]int 是兩種不同的類型。

          // 數(shù)組比較
          a1 := [2]int{12}
          a2 := [...]int{12}
          a3 := [2]int{13}
          // 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)
          }

          值類型

          Go 數(shù)組是值類型,賦值和傳參都會復(fù)制整個(gè)數(shù)組。

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

          package main

          import "fmt"

          func main() {
              // 數(shù)組復(fù)制
              x := [2]int{1020}
              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]
          }

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

          package main

          import "fmt"

          func main() {
              x := [2]int{1020}

              // 傳參
              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)容并沒有變化。

          那么,有沒有可能在函數(shù)內(nèi)修改,而影響到函數(shù)外呢?答案是可以的,接下來要說的切片就可以做到。

          切片 slice

          切片是一種引用類型,它有三個(gè)屬性:指針,長度和容量。

          1. 指針:指向 slice 可以訪問到的第一個(gè)元素。

          2. 長度:slice 中元素個(gè)數(shù)。

          3. 容量:slice 起始元素到底層數(shù)組最后一個(gè)元素間的元素個(gè)數(shù)。

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

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

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

          底層是一個(gè)包含 10 個(gè)整型元素的數(shù)組,data1 指向數(shù)組第 4 個(gè)元素,長度是 3,容量取到數(shù)組最后一個(gè)元素,是 7。data2 指向數(shù)組第 5 個(gè)元素,長度是 4,容量是 6。

          創(chuàng)建切片

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

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

          // 基于數(shù)組創(chuàng)建切片
          var array = [...]int{12345678}

          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 來創(chuàng)建:

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

          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)
          }

          比較

          不能使用 == 來測試兩個(gè) slice 是否有相同元素,但 slice 可以和 nil 比。slice
          類型的零值是 nil,表示沒有對應(yīng)的底層數(shù)組,而且長度和容量都是零。

          但也要注意,長度和容量都是零的,其值也并不一定是 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, 1011)
          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{1213}
          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{12345}
          s9 := []int{543}
          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]

          引用類型

          上文介紹數(shù)組時(shí)說過,數(shù)組屬于值類型,所以在傳參時(shí)會復(fù)制整個(gè)數(shù)組內(nèi)容,如果數(shù)組很大的話,是很影響性能的。而傳遞切片只會復(fù)制切片本身,并不影響底層數(shù)組,是很高效的。

          package main

          import "fmt"

          func main() {
              s9 := []int{543}

              // 傳參
              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 中修改的值會影響到 main 中。

          總結(jié)

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

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

          在函數(shù)傳參過程中,數(shù)組如果很大的話,很影響效率,而切片則解決了這個(gè)問題,效率更高。

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


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

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

          關(guān)注公眾號 AlwaysBeta,回復(fù)「goebook」領(lǐng)取 Go 編程經(jīng)典書籍。

          Go 專欄文章列表:

          1. Go 專欄 | 開發(fā)環(huán)境搭建以及開發(fā)工具 VS Code 配置

          2. Go 專欄 | 變量和常量的聲明與賦值

          3. Go 專欄|基礎(chǔ)數(shù)據(jù)類型:整數(shù)、浮點(diǎn)數(shù)、復(fù)數(shù)、布爾值和字符串

          瀏覽 30
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  性v天堂 | 免费操逼。| 9.1成长蘑菇视频 | 99热在线精品免费 | 爆乳一区新 |