Go slice of the four sacred animal

Slice Slice

Primer

Think about a problem in the Go language already exists array types, and why do you need a slice type it? Therefore, we need to understand the limitations of array type properties brought about by:

  1. When defining the array must be a good its predetermined length and type can not be changed, the type of data types including length +

  2. When the array as a parameter, the argument must be an array of the same length which is the same type

  3. When a member of the array is full, you can not add new members

    Therefore, most of the reason is sliced ​​out the birth address the limitations of arrays

Three Questions soul

  1. What is a slice?

    Slice Sliceis a sequence of variable length have the same data element type

    Slices are made based on one array type package

    Slice support automatic expansion

    Slice is a reference type, including its internal structure 地址 长度and容量

    Slice length and capacity

    Slice has its own length and capacity, we can use the built- len()seeking function of the length, use the built-in cap()function evaluation slice capacity

    Slice of nature

    Essence of the slice is encapsulated array

    It contains three information: pointers underlying array, the length of the slice, the slice of capacity

    Case

    A conventional array a := [8]int[0,1,2,3,4,5,6,7,8], the slice s1 := a[:5]shown below

slice s2 := a[3:6]

  1. Slice is doing?

    Slice generally used for fast integration of a data set

  2. How to use a slice?

    Defined sections

    var name []T // name 代表变量名   // T 表示切片中的元素类型
    func main() {
     // 声明切片类型
     var a []string              //声明一个字符串切片
     var b = []int{}             //声明一个整型切片并初始化
     var c = []bool{false, true} //声明一个布尔切片并初始化
     var d = []bool{false, true} //声明一个布尔切片并初始化
     fmt.Println(a)              //[]
     fmt.Println(b)              //[]
     fmt.Println(c)              //[false true]
     fmt.Println(a == nil)       //true
     fmt.Println(b == nil)       //false
     fmt.Println(c == nil)       //false
     // fmt.Println(c == d)   //切片是引用类型,不支持直接比较,只能和nil比较
    }

    Based on the definition of an array slice

    Since the primary is an array of slices, so we can re-define array-based

    func main() {
     // 基于数组定义切片
     a := [5]int{55, 56, 57, 58, 59}
     b := a[1:4]                     //基于数组a创建切片,包括元素a[1],a[2],a[3]
     fmt.Println(b)                  //[56 57 58]
     fmt.Printf("type of b:%T\n", b) //type of b:[]int
    }
    c := a[1:] //[56 57 58 59]
    d := a[:4] //[55 56 57]
    e := a[:]  //[55 56 57 58 59]

    Then slice sliced

    In addition to the array obtained by slicing, the slice can be obtained by slicing

    func main() {
     //切片再切片
     a := [...]string{"北京", "上海", "广州", "深圳", "成都", "重庆"}
     fmt.Printf("a:%v type:%T len:%d  cap:%d\n", a, a, len(a), cap(a))
     b := a[1:3]
     fmt.Printf("b:%v type:%T len:%d  cap:%d\n", b, b, len(b), cap(b))
     c := b[1:5]
     fmt.Printf("c:%v type:%T len:%d  cap:%d\n", c, c, len(c), cap(c))
    }

    Export

    a:[北京 上海 广州 深圳 成都 重庆] type:[6]string len:6  cap:6
    b:[上海 广州] type:[]string len:2  cap:5
    c:[广州 深圳 成都 重庆] type:[]string len:4  cap:4

    When using sections were then sliced, the index can not exceed the original length of the array, or an error occurs in the index out of bounds

    Use make()Constructor sliced

    make([]T, size, cap) // T 切片的元素类型    // size 切片中元素的数量    //cap 切片的容量
    func main() {
     a := make([]int, 2, 10)
     fmt.Println(a)      //[0 0]
     fmt.Println(len(a)) //2
     fmt.Println(cap(a)) //10
    }

    Can not be directly compared between slices

    Between slices is not directly comparable, we can not use the ==operator to determine whether the element contains two identical sections

    Slice the only legitimate comparison between the operation and are nilrelatively

    A nilslice value is not the underlying array

    ​ 一个 nil 值的切片的长度和容量都是 0

    ​ 一个长度和容量都是 0 的切片不一定就是 nil

    var s1 []int         //len(s1)=0;cap(s1)=0;s1==nil
    s2 := []int{}        //len(s2)=0;cap(s2)=0;s2!=nil
    s3 := make([]int, 0) //len(s3)=0;cap(s3)=0;s3!=nil
    >> 要判断一个切片是否为空,要用 len(s) == 0 来判断,不能用 s == nil 来判断

    切片的赋值拷贝

    拷贝前后的变量共享底层数组,对一个切片的修改会影响到另一个切片的内容

    func main() {
     s1 := make([]int, 3) //[0 0 0]
     s2 := s1             //将s1直接赋值给s2,s1和s2共用一个底层数组
     s2[0] = 100
     fmt.Println(s1) //[100 0 0]
     fmt.Println(s2) //[100 0 0]
    }

    切片遍历

    func main() {
     s := []int{1, 3, 5}
    
     for i := 0; i < len(s); i++ {
         fmt.Println(i, s[i])
     }
    
     for index, value := range s {
         fmt.Println(index, value)
     }
    }

    添加元素

    Go 语言的内建函数 append() 可以为切片动态添加元素

    每个切片都会指向一个底层数组,这个数组能容纳一定数量的元素,当底层数组不能容纳新增的元素时,切片就会自动按照一定的策略进行“扩容”,此时切片指向的底层数组就会更换

    “扩容”操作旺旺发生在 append() 函数调用时

    func main() {
     //append()添加元素和切片扩容
     var numSlice []int
     for i := 0; i < 10; i++ {
         numSlice = append(numSlice, i)
         fmt.Printf("%v  len:%d  cap:%d  ptr:%p\n", numSlice, len(numSlice), cap(numSlice), numSlice)
     }
    }

    输出

    [0]  len:1  cap:1  ptr:0xc0000a8000
    [0 1]  len:2  cap:2  ptr:0xc0000a8040
    [0 1 2]  len:3  cap:4  ptr:0xc0000b2020
    [0 1 2 3]  len:4  cap:4  ptr:0xc0000b2020
    [0 1 2 3 4]  len:5  cap:8  ptr:0xc0000b6000
    [0 1 2 3 4 5]  len:6  cap:8  ptr:0xc0000b6000
    [0 1 2 3 4 5 6]  len:7  cap:8  ptr:0xc0000b6000
    [0 1 2 3 4 5 6 7]  len:8  cap:8  ptr:0xc0000b6000
    [0 1 2 3 4 5 6 7 8]  len:9  cap:16  ptr:0xc0000b8000
    [0 1 2 3 4 5 6 7 8 9]  len:10  cap:16  ptr:0xc0000b8000

    结论:

    1. append() 函数将元素追加到切片的最后并返回该切片
    2. 切片 numSlice 的容量按照 1, 2, 4, 8, 16这样的规则自动就行扩容,每次扩容后都是扩容前的两倍

    append 支持一次添加多个元素

    var citySlice []string
    // 追加一个元素
    citySlice = append(citySlice, "北京")
    // 追加多个元素
    citySlice = append(citySlice, "上海", "广州", "深圳")
    // 追加切片
    a := []string{"成都", "重庆"}
    citySlice = append(citySlice, a...)
    fmt.Println(citySlice) //[北京 上海 广州 深圳 成都 重庆]

    复制切片(独立的切片)

    抛出一个小问题:

    ​ 由于切片是引用类型,a与b实际上引用的是同一个内存地址,所以修改b切片的同时a切片的值也会同时改变

    func main() {
       a := []int{1, 2, 3, 4, 5}
       b := a
       fmt.Println(a) //[1 2 3 4 5]
       fmt.Println(b) //[1 2 3 4 5]
       b[0] = 1000
       fmt.Println(a) //[1000 2 3 4 5]
       fmt.Println(b) //[1000 2 3 4 5]
    }

    如何实现修改切片b切片a 保持不变?

    copy() 函数的使用
    copy(destSlice, srcSlice []T)    // destSlice 目标切片   // srcSlice 数据来源切片
    func main() {
     // copy()复制切片
     a := []int{1, 2, 3, 4, 5}
     c := make([]int, 5, 5)
     copy(c, a)     //使用copy()函数将切片a中的元素复制到切片c
     fmt.Println(a) //[1 2 3 4 5]
     fmt.Println(c) //[1 2 3 4 5]
     c[0] = 1000
     fmt.Println(a) //[1 2 3 4 5]
     fmt.Println(c) //[1000 2 3 4 5]
    }

    删除元素

    Go 语言中没有删除切片元素的专门方法

    func main() {
       // 从切片中删除元素
       a := []int{30, 31, 32, 33, 34, 35, 36, 37}
       // 要删除索引为2的元素
       a = append(a[:2], a[3:]...)
       fmt.Println(a) //[30 31 33 34 35 36 37]
    }

    通过修改切片的整体值来实现元素的删除,如从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

Guess you like

Origin www.cnblogs.com/Ethan99/p/10979753.html