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

          Golang數(shù)據(jù)類型之切片

          共 6741字,需瀏覽 14分鐘

           ·

          2021-07-03 13:22

          目錄

          • 1、切片介紹

          • 2、聲明和初始化

            • 2.1 make 創(chuàng)建

            • 2.2 字面量創(chuàng)建

            • 2.3 創(chuàng)建數(shù)組和切片的區(qū)別

            • 2.4 創(chuàng)建切片的本質(zhì)

          • 3、切片訪問

          • 4、nil 和空切片

          • 5、切片中添加元素

          • 6、通過切片創(chuàng)建切片

          • 7、切片遍歷

          • 8、切片拷貝

          • 9、切片作為函數(shù)參數(shù)



          1、切片介紹

          Go中的切片slice依賴于數(shù)組,它的底層就是數(shù)組,所以數(shù)組具有的優(yōu)點,slice都有。且slice支持可以通過appendslice中追加元素,長度不夠時會動態(tài)擴展,通過再次slice切片,可以得到得到更小的slice結(jié)構(gòu),可以迭代、遍歷等

          // runtime/slice.go
          type slice struct {
              array unsafe.Pointer // 數(shù)組指針
              len   int // 長度
              cap   int // 容量
          }

          每一個slice結(jié)構(gòu)都由 3 部分組成:

          • 容量(capacity):即底層數(shù)組的長度,表示這個slice目前最多能擴展到的長度
          • 長度(length):表示slice當前的長度,即當前容納的元素個數(shù)
          • 數(shù)組指針(array):指向底層數(shù)組的指針

          比如創(chuàng)建一個長度為3,容量為5int類型的切片

          s := make([]int34)
          fmt.Println(a, len(s), cap(s)) // [0 0 0] 3 5

          2、聲明和初始化

          Go中可以通過多種方式創(chuàng)建和初始化切片

          是否提前知道切片所需的容量通常會決定如何創(chuàng)建切片

          2.1 make 創(chuàng)建

          // 創(chuàng)建一個整型切片, 其長度為 3 個元素,容量為 5 個元素
          slice := make([]int35)

          // 我們也可以省略容量, 默認長度==容量
          // 創(chuàng)建一個整型切片 其長度和容量都是 5 個元素
          slice := make([]int5)

          // 但是長度不能小于容量, 否則編譯器過不了
          // a := make([]int, 5, 3)

          2.2 字面量創(chuàng)建

          // 這種方法和創(chuàng)建數(shù)組類似,只是不需要指定[]運算符里的值。初始的長度和容量會基于初始化時提供的元素的個數(shù)確定
          slice := []int{1,2,3}

          // 和數(shù)組一樣也可以通過指定索引初始化, 比如index 4 值為100
          slice := []int{3100}

          2.3 創(chuàng)建數(shù)組和切片的區(qū)別

          如果在[]運算符里指定了一個值,那么創(chuàng)建的就是數(shù)組而不是切片,比如

          a := [3]int{1,2,3}
          b := []int{1,2,3}

          雖然他們聲明時只要這一點點區(qū)別,但是他們的數(shù)據(jù)結(jié)構(gòu)區(qū)差別卻很大,一個是引用類型一個是值類型

          2.4 創(chuàng)建切片的本質(zhì)

          切片相關(guān)源碼放置位置: src/runtime/slice.go, 我們使用make時, 實際上是調(diào)用的makeslice函數(shù)

          // 這里一波操作過后返回的是slice的pointer
          func makeslice(et *_type, lencap int) unsafe.Pointer {}

          3、切片訪問

          對切片里某個索引指向的元素賦值和對數(shù)組里某個索引指向的元素賦值的方法完全一樣。使 用[]操作符就可以改變某個元素的值,下面是使用切片字面量來聲明切片

          s := []int{1,2,3}
          s[0]

          // 但是不能越界訪問, 比如
          s[3// panic: runtime error: index out of range [3] with length 3

          查看切片長度: len

          查看切片容量: cap

          4、nil 和空切片

          聲明未初始化的切片為nil

          var s []int
          var s1 []int
          fmt.Printf("%p\n", s1)  // 0x0

          make初始化的是一個空切片

          s := make([]int,0)
          // unsafe.Pointer ——> *slice
          s2 := make([]int0)
          fmt.Printf("%p\n", s2)  // 0x126c9

          所以nil切片直接賦值是要報錯的

          var s []int
          s[0] = 1 // panic: runtime error: index out of range [0] with length 0

          5、切片中添加元素

          通過append函數(shù)往切片中追加元素, 比如

          s := make([]int04)
          s = append(s, 10203040)

          現(xiàn)在底層數(shù)組已經(jīng)滿了,再往里面追加元素會如何?

          s = append(s,50)

          函數(shù)append()會智能地處理底層數(shù)組的容量增長。在切片的容量小于1024個元素時,總是會成倍地增加容量。一旦元素個數(shù)超過1024,容量的增長因子會設(shè)為1.25,也就是會每次增加25%的容量(隨著語言的演化,這種增長算法可能會有所改變)

          因此擴容對于切片來說是一個比較消耗成本的事情,會開辟新的內(nèi)存空間

          擴容時是新創(chuàng)建一個切片數(shù)組,若原數(shù)據(jù)沒有繼續(xù)使用,會被gc

          s1 := make([]int04)
          s1 = append(s1, 10203040// 10, 20, 30, 40
          fmt.Println(s1, len(s1), cap(s1))  // [10 20 30 40] 4 4
          s1 = append(s1, 50)
          fmt.Println(s1, len(s1), cap(s1))  // [10 20 30 40 50] 5 8

          6、通過切片創(chuàng)建切片

          切片之所以被稱為切片,是因為創(chuàng)建一個新的切片,也就是把底層數(shù)組切出一部分。通過切片創(chuàng)建新切片的語法如下, 詳情請參考: 切片的語法

          slice[low : high]
          slice[low : high : max]
          • low : 表示從slic的第幾個元素開始切
          • high : 控制切片的長度high-low
          • max : 控制切片的容量max-low

          比如

          s1 := []int{1234}
          s2 := s1[2:4:4]  // [index2, index4) 左閉右開區(qū)間, 容量 4-2
          fmt.Println(s2, len(s2), cap(s2)) // [3 4] 2 2

          如果high == max也可以省略max,比如:

          s3 := s1[2:4]

          再次基礎(chǔ)上還要幾種省略寫法:

          • 省略 low:表示從index 0開始
          • 省略 high:表示到結(jié)尾len
          • 省略 max :表示到結(jié)尾len
          • 都省略:等于復制
          slice[i:]  // 從 i 切到最尾部
          slice[:j]  // 從最開頭切到 j(不包含 j)
          slice[:]   // 從頭切到尾,等價于復制整個 slice

          注意: 通過切片創(chuàng)建出來的切片是共享底層數(shù)據(jù)結(jié)構(gòu)的(數(shù)組)

          共享底層數(shù)組會導致相互影響, 比如修改原切片會影響多所有復制出來的切片

          s1 := []int{10203040}
          s2 := s1[1:3]
          fmt.Println(s2, len(s2), cap(s2))

          fmt.Println(s1[1], s2[0])
          s1[1] = 200
          fmt.Println(s1[1], s2[0])

          有擴容的原理也可以知道,當擴容后,就不共享底層數(shù)組了,比如:

          s1 := []int{10203040}
          s2 := s1[1:3:3]
          fmt.Println(s2, len(s2), cap(s2))

          fmt.Println(s1[1], s2[0])
          s2 = append(s2, 30)   // s2 擴容
          s1[1] = 200           // 修改s1
          fmt.Println(s1[1], s2[0]) // s1修改并不會影響s2

          因此,一般不要修改切片,如果要修改請使用后面的深拷貝復制一個全新的切片

          7、切片遍歷

          切片是一個集合,可以迭代其中的元素。Go有個特殊的關(guān)鍵字range,它可以配合關(guān)鍵字for來迭代切片里的元素

          func TestSliceAppend1(t *testing.T) {
           s := make([]int04)
           s = append(s, 10203040)
           for i, v := range s {
            fmt.Println(i, v)
           }
           /*
           0 10
           1 20
           2 30
           3 40
            */

          }

          這種方式底層的實現(xiàn),也是拷貝一份切片提供給循環(huán)使用,因此同樣會帶來開銷

          當?shù)衅瑫r,關(guān)鍵字range會返回兩個值。第一個值是當前迭代到的索引位置,第二個值是該位置對應元素值的一份副本。需要強調(diào)的是,range創(chuàng)建了每個元素的副本,而不是直接返回對該元素的引用。要想獲取每個元素的地址,可以使用切片變量和索引值

          8、切片拷貝

          不能像數(shù)組一樣直接使用賦值語句來拷貝一個切片,因為數(shù)組是值,而切片是指針, 真正的數(shù)據(jù)維護在底層數(shù)組里面

          a1 := [2]{1,2}
          a2 := a1    // 值拷貝, a1, a2 互不影響

          s1 := []{12}
          s2 := s1   // 指針拷貝 s1, s2 指向同一*slice結(jié)構(gòu)體, 就是一個東西,等于沒拷貝

          Go內(nèi)置的copy()函數(shù)可以將一個切片中的元素拷貝到另一個切片中,其函數(shù)聲明為

          func copy(dst, src []Type) int

          它表示把切片src中的元素拷貝到切片dst中,返回值為拷貝成功的元素個數(shù)。如果srcdst長,就截斷;如果srcdst短,則只拷貝src那部分

          s1 := []int{10203040}
          s2 := make([]int5)
          num := copy(s2, s1)   // 這時候s1 和 s2 就是2個切片,包含底層數(shù)據(jù), 互不影響
          fmt.Println(num)  // 4
          fmt.Println(s1, s2)  // [10 20 30 40] [10 20 30 40 0]
          s1[0] = 100
          fmt.Println(s1[0], s2[0])  // 100 10

          9、切片作為函數(shù)參數(shù)

          函數(shù)在調(diào)用傳參時,都是值拷貝

          切片的本質(zhì)是指針,如果是切片作為函數(shù)的參數(shù)調(diào)用,則拷貝的是指針的地址

          因此切片作為函數(shù)的參數(shù)時,最大的好處是傳遞效率高

          因此切片的用法遠多于數(shù)組,數(shù)組用來定義底層的數(shù)據(jù)結(jié)構(gòu)

          func TestSliceMain2(t *testing.T) {
           s1 := make([]int04)
           s1 = append(s1, 10203040// 10, 20, 30, 40
           fmt.Println(Sum1(s1))  // 100
          }

          func Sum1(args []int) int {
           sum := 0
           for _, v := range args {
            sum += v
           }
           return sum
          }
          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  大美女人人操人人摸 | 久久久久无码精品国产sm大站 | 在线韩国精品三级中文hd无码精品 | 成人电影综合网 | 日韩高清无码小电影 |