【go語言學習】切片slice

go語言中數組的長度是固定的并且數組長度屬于類型的一部分,所以數組有很多的局限性。
go提供了一種更加靈活強悍的內置類型切片(“動態數組”)。
切片(Slice)是一個擁有相同類型元素的可變長度的序列。它是基于數組類型做的一層封裝。它非常靈活,支持自動擴容。
切片是一個引用類型,它的內部結構包含地址長度容量

一、切片的聲明和初始化

1、直接聲明切片

var name []T
其中:name-切片的名稱
???????????T-切片的類型

package main

import (
    "fmt"
)

func main() {
    var numSlice = []int{1, 3, 5, 7, 9}
    fmt.Println(numSlice)
    var stringSlice []string
    stringSlice = []string{"北京", "上海", "深圳", "蘇州"}
    fmt.Println(stringSlice)
}

運行結果

[1 3 5 7 9]
[北京 上海 深圳 蘇州]
2、基于數組創建切片

var slice = arr[low: high]
其中:表達式中的low和high表示一個索引范圍(左包含,右不包含)

package main

import (
    "fmt"
)

func main() {
    var numArry = [5]int{1, 2, 3, 4, 5}
    var numSlice = numArry[2:4]
    fmt.Println(numSlice)
}

運行結果

[3 4]
3、使用make()函數構造切片

var slice = make([]T, len, cap)
其中:T-切片的元素類型
???????????len-切片的長度
???????????cap-切片的容量

package main

import (
    "fmt"
)

func main() {
    var intSlice = make([]int, 2, 4)
    fmt.Println(intSlice)
    fmt.Println(len(intSlice))
    fmt.Println(cap(intSlice))
}

運行結果

[0 0]
2
4

二、切片的本質

切片的本質就是對底層數組的封裝,它包含了三個信息:底層數組的指針、切片的長度(len)和切片的容量(cap)
舉例:
數組 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[0:5],相應示意圖如下:



切片s2 := a[3:6],相應示意圖如下:


對slice所做的任何修改都將反映在底層數組中。
當多個切片共享相同的底層數組時,每個切片對元素所做的更改將在數組中反映出來。

package main

import (
    "fmt"
)

func main() {
    arr := [8]int{0, 1, 2, 3, 4, 5, 6, 7}
    fmt.Println("操作前:", arr)
    slice := arr[:5]
    fmt.Println("操作前:", slice)
    slice[0] = 100
    fmt.Println("操作后:", arr)
    fmt.Println("操作后:", slice)
}

運行結果

操作前: [0 1 2 3 4 5 6 7]
操作前: [0 1 2 3 4]
操作后: [100 1 2 3 4 5 6 7]
操作后: [100 1 2 3 4]

三、切片的操作

1、切片的遍歷

切片的遍歷方式和數組是一致的,支持索引遍歷和for range遍歷。

package main

import (
    "fmt"
)

func main() {
    slice := []int{0, 1, 2, 3, 4, 5, 6, 7}
    for i := 0; i < len(slice); i++ {
        fmt.Printf("[%v-%v]\t", i, slice[i])
    }
    fmt.Println("")
    for k, v := range slice {
        fmt.Printf("[%v-%v]\t", k, v)
    }
}

運行結果

[0-0]   [1-1]   [2-2]   [3-3]   [4-4]   [5-5]   [6-6]   [7-7]   
[0-0]   [1-1]   [2-2]   [3-3]   [4-4]   [5-5]   [6-6]   [7-7]
2、切片的復制拷貝
  • 直接賦值拷貝,淺拷貝
    拷貝前后兩個變量共享底層數組,對一個切片的修改會影響另一個切片的內容
package main

import (
    "fmt"
)

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

運行結果

slice1: [100 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4 5 6 7]
  • 使用copy()函數操作,深拷貝

copy(destSlice, srcSlice)
其中:destSlice-目標切片
???????????srcSlice-源切片

package main

import (
    "fmt"
)

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

運行結果

slice2: [0 1 2 3 4]
slice1: [0 1 2 3 4 5 6 7]
slice2: [100 1 2 3 4]
3、向切片中添加元素append

Go語言的內建函數append()可以為切片動態添加元素。 可以一次添加一個元素,可以添加多個元素,也可以添加另一個切片中的元素(后面加…)

slice = append(slice, num1, num2)
slice = append(slice, slice...)

  • append函數會改變slice所引用的數組的內容,從而影響到引用同一數組的其它slice。
  • 但當slice中沒有剩余空間(即(cap-len) == 0)時,切片就會自動按照一定的策略進行“擴容”,此時將動態分配新的數組空間。返回的slice數組指針將指向這個空間,而原數組的內容將保持不變;其它引用此數組的slice則不受影響。
package main

import (
    "fmt"
)

func main() {
    arr := [5]int{0, 1, 2, 3, 4}
    slice := arr[:3]
    slice = append(slice, 100)
    fmt.Println("arr:", arr)
    fmt.Println("slice:", slice)
    fmt.Println("------------------")
    slice = append(slice, 200)
    fmt.Println("arr:", arr)
    fmt.Println("slice:", slice)
    fmt.Println("------------------")
    slice = append(slice, 300)
    fmt.Println("arr:", arr)
    fmt.Println("slice:", slice)
}

運行結果

arr: [0 1 2 100 4]
slice: [0 1 2 100]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200]
------------------
arr: [0 1 2 100 200]
slice: [0 1 2 100 200 300]
4、從切片中刪除元素

Go語言中并沒有刪除切片元素的專用方法,我們可以使用切片本身的特性來刪除元素。

slice = append(slice[:index], slice[index+1:]...)

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}
    slice = append(slice[:3], slice[4:]...)
    fmt.Println(slice)
}

運行結果

[1 2 3 5]
5、注意事項
  • 切片不能進行比較,切片唯一合法的比較操作是和nil比較。
  • 判斷切片是否是空的,要使用len(slice) == 0來判斷,不應該使用slice == nil來判斷
package main

import "fmt"

func main() {
    var s1 []int
    s2 := []int{}
    s3 := make([]int, 0, 0)
    fmt.Printf("s1: len = %v, cap = %v, isNil:%v\n", len(s1), cap(s1), s1 == nil)
    fmt.Printf("s2: len = %v, cap = %v, isNil:%v\n", len(s2), cap(s2), s2 == nil)
    fmt.Printf("s3: len = %v, cap = %v, isNil:%v\n", len(s3), cap(s3), s3 == nil)
}

運行結果

s1: len = 0, cap = 0, isNil:true
s2: len = 0, cap = 0, isNil:false
s3: len = 0, cap = 0, isNil:false
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。