• Go语言


    引子

    因为数组的长度是固定的并且数组长度属于类型的一部分,所以数组有很多的局限性。 例如:

    func arraySum(x [3]int) int{
        sum := 0
        for _, v := range x{
            sum = sum + v
        }
        return sum
    }

    这个求和函数只能接受[3]int类型,其他的都不支持。 再比如,

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

    数组a中已经有三个元素了,我们不能再继续往数组a中添加新元素了。

    切片

    切片(Slice)是一个拥有相同类型元素的可变长度的序列。它是基于数组类型做的一层封装。它非常灵活,支持自动扩容。

    切片是一个引用类型,它的内部结构包含地址长度容量。切片一般用于快速地操作一块数据集合。

    值类型与引用型的区别:

    底层数组的修改会影响切片
    切片的修改也会影响底层数组

     切片的定义

    var name []T
    // 切片拥有数组的所有特性
    • name:表示变量名
    • T:表示切片中的元素类型

    切片的声明

    package main
    
    import "fmt"
    
    func main()  {
        // 切片声明方式一  直接声明
        var s = []int{1,2,3}
        fmt.Println(s)  // [1 2 3]
        fmt.Printf("%T 
    ", s)  // []int
    
        // 切片声明方式二 从数组中得到切片
        var a = [3]int{1,2,3}
        c := a[0:3]
        fmt.Println(c)  // [1 2 3]
        fmt.Printf("%T 
    ", c)  // []int
    
    }

    切片的长度和容量

    切片拥有自己的长度和容量,我们可以通过使用内置的len()函数求长度,使用内置的cap()函数求切片的容量。

    package main
    
    import "fmt"
    
    func main()  {
        var a1 = [...]string{"北京", "上海", "深圳", "成都", "广州", "青岛"}
        s1 := a1[1:4]
        // 切片的大小 (切片内目前元素的数量)
        fmt.Println(len(s1))  // 3
        // 切片的容量 (底层数组最大能放多少元素)
        fmt.Println(cap(s1))  // 5  
    
    }

    切片的本质

    切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。

    举个例子,现在有一个数组a := [8]int{0, 1, 2, 3, 4, 5, 6, 7},切片s1 := a[:5],相应示意图如下。slice_01切片s2 := a[3:6],相应示意图如下:slice_02

    append()方法为切片添加元素

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

    append()函数将元素追加到切片的最后并返回该切片。

    切片numSlice的容量按照1,2,4,8,16这样的规则自动进行扩容,每次扩容后都是扩容前的2倍

    切片的扩容策略

    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // Check 0 < newcap to detect overflow
            // and prevent an infinite loop.
            for 0 < newcap && newcap < cap {
                newcap += newcap / 4
            }
            // Set newcap to the requested cap when
            // the newcap calculation overflowed.
            if newcap <= 0 {
                newcap = cap
            }
        }
    }
    源码
    • 首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。
    • 否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap),
    • 否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)
    • 如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。

    需要注意的是,切片扩容还会根据切片中元素的类型不同而做不同的处理,比如intstring类型的处理方式就不一样

    copy()函数复制切片

    Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:

    copy(destSlice, srcSlice []T)
    • srcSlice: 数据来源切片
    • destSlice: 目标切片

    使用

    package main
    
    import "fmt"
    
    func main{
        test()
    }
    func test()  {
        a := []int{1,2,3}
        b := a  // 直接赋值
    
        var c []int  // 定义一个切片,还没有申请内存
        c = make([]int, 3, 3)  // 为c切片申请内存 make([]T, len, cap)
        copy(c, a)  // 参数2是被copy对象
    
        b[0] = 100
        fmt.Println(a)  // [100 2 3]
        fmt.Println(b)  // [100 2 3]
        fmt.Println(c)  // [1 2 3]
    
    }
    var c []int  // 只定义,没初始化,没有内存
    var c = []int{} // 定义,初始化,有内存

    从切片中删除元素

    Go语言中并没有删除切片元素的专用方法,我们可以使用切片本身的特性来删除元素。 代码如下:

    package main
    
    import "fmt"
    
    func main{
        test1()
    }
    func test1()  {
        a := []int{1,2,3,4,5,6,7}
        a = append(a[:2], a[3:]...)
        fmt.Println(a)  // [1 2 4 5 6 7]
    }

    总结一下就是:要从切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)

     题

    package main
    
    import "fmt"
    
    func main{
        test2()
    }
    func test2()  {
        a := [...]int{1,2,3,4,5,6,7}
        b := a[:]
        b[0] = 100
        fmt.Println(a[0])  // 100  因为切片是引用类型
    
        c := a[2:5]
        d := c[:5]
        fmt.Println(c)  // [3 4 5]
        fmt.Println(len(c))  // 3
        fmt.Println(cap(c))  // 5
        fmt.Println(d)  // [3 4 5 6 7]
        fmt.Println(len(d))  // 5
        fmt.Println(cap(d))  // 5
    }
    c与d内存地址一样,因为c与d的第一个元素是同一个

     

    摘自

  • 相关阅读:
    小程序获知渠道弹出层设计
    小程序下拉菜单筛选
    align-conten和align-items的区别
    微信小程序文本如何换行
    微信小程序最新获取用户头像昵称的方法
    JQ版本对比
    选择收货地址列表的某一项将数据传到订单页面
    inline、block、inline-block属性的区别
    微博资料
    zookeeper知识点学习
  • 原文地址:https://www.cnblogs.com/waller/p/11923747.html
Copyright © 2020-2023  润新知