美文网首页趣味学习Golang快速上手
Go切片:二级跳实现优雅的“数组”

Go切片:二级跳实现优雅的“数组”

作者: mick_ | 来源:发表于2019-06-20 20:56 被阅读0次

切片是一种数据结构,便于使用和管理数据集合,切片是基于数组的概念构成,可以按需自动增长和缩小,切片底层也会是在连续块中分配的,所以切片还能获得索引,迭代。

内部实现

// 切片是一个很小的对象,对底层数组进行了抽象,切片有三个字段组成如图所示
// runtime/slice.go
type slice struct {
    array unsafe.Pointer `description:"元素指针"`
    len   int            `description:"长度"`
    cap   int            `description:"容量"`
}
image.png

切片的创建于初始化

  • 创建切片
    // TODO 使用var关键字创建
    // var是关键字表示声明变量
    // slice 是变量的名称自定义(字母,数组,下划线,不能用数字打头)
    // []int 表示切片的类型,与数组类似,只是中括号中不用写数字,写了就是数组,不写就是切片
    var slice []int

    // TODO  使用内置函数make创建一个切片
    // 第一个参数( []int)表示切片的类型,此示例是整形
    // 第二个参数 (3) 表示切片的长度,此示例是3
    // 第三个参数 (5) 表示切片的容量,容量必须大于等于长度,这个数值可以省略,当省略的时候容量与长度相等,此示例是5
    slice := make([]int,3,5)

    // TODO 通过切片字面量创建
    // 创建方式与数组一致,可指定下标,也可不指定
    // 指定下标如  []int{2:20,30},表示下标为2的值是20,下标为3的值是30,索引从0开始,未指定的使用类型的零值初始化
    slice :=  []int{}
  • nil与空切片的对比与区别


    image.png
    image.png

nil切片与空切片的区别
1:nil切片的值与nil比较是true ,空切片与nil比较是false
2:建议使用nil切片效率高

  • 几种声明方式对比
优势 var make 字面量
是否可声明
初始化值 nil 类型零值 类型零值+指定下标值
指定下标赋值
指定长度 0 声明的长度 根据下标与实际值决定
指定容量 0 同长度或指定值 同长度

使用切片

// 切片的截取如   slice[1:3:5] ,左闭右开法则[1,3)
// 第一个参数 表示从哪一个下标开始截取
// 第二个参数 表示截取到哪一位(切记不包含此下标)
// 第三个参数 表示容量截取到哪一位
// 新切片与原始的切片之间共享底层数组,除非两个切片发生扩容(当切片的长度大于容量的时候,必然扩容,因为切片是动态数组)
// 创建一个切片
slice := []int{10,20,30,40,50}
// 从其他切片中重新生成一个切片
// 长度是2,容量是3
newSlice := slice[1:3:5]
image.png
func main() {
    s := []int{1, 2, 3, 4}
    fmt.Printf("%p\n", s)
    fmt.Printf("%p\n", &s)
    sliceFirstAddr(s)
}
// @title  输出切片的第一个值的地址
// @description   2019/6/27   14:33  mick  
// @param   id       int      "id"
// @return  err      error    "错误"
// TODO   直接切片值的地址
// TODO   判断切片的长度是否小于1
// TODO   输出切片第一个位置的元素地址
func sliceFirstAddr(s []int){
    fmt.Printf("%p\n", s)
    if len(s) <1 {
        return
    }
    fmt.Printf("%p\n", &s[0])
}

切片扩容(重点)

切片是一种动态数组,既然是动态的,那么就可以实现数据集合的增加与删除,切片使用append函数进行数据的追加,使用切割实现数据的切分,当切片的长度大于容量的时候,切片会自动扩容

  • 第一种扩容策略-双倍扩容
    1.切片每次新增个数不超过原来的1倍,
    1.且每次增加数不超过1024个,
    2.且增加后总长度小于1024个,
    3.这种情况下扩容后为原来的2倍
// @title   切片的双倍扩容
// @description   2019/6/27   15:07  mick  
// @param   void
// @return  void
// TODO   使用make函数创建一个空切片(不是nil切片)
// TODO   打印当前的容量
// TODO   循环追加数据
// TODO   每次循环打印切片的长度与容量
func doubleIncrease() {
    s1 := make([]int, 0)
    fmt.Printf("The capacity of s1: %d\n", cap(s1))
    for i := 1; i <= 17; i++ {
        s1 = append(s1, i)
        fmt.Printf("s1(%d): len: %d, cap: %d\n", i, len(s1), cap(s1))
    }
}

  • 第二种扩容策略-微扩容
    1.切片一次新增个数超过原来1倍
    2.但不超过1024个
    3.且增加后总长度小于1024个
    4.这种情况下扩容后比实际具有的总长度还要大一些。
// @title  容量微扩增
// @description   2019/6/27   15:12  mick  
// @param   void
// @return  void
// TODO   使用make函数创建一个切片
// TODO   打印此切片的容量
// TODO   使用append函数追加5个元素,并赋值给新切片
// TODO   打印此切片的长度与容量
// TODO   使用append函数追加11个元素,并赋值给新切片
// TODO   打印此切片的长度与容量
func littleIncrease(){
    s2 := make([]int, 10)
    fmt.Printf("The capacity of s2: %d\n", cap(s2))
    r1 := append(s2, make([]int, 5)...)
    fmt.Printf("r1: len: %d, cap: %d\n", len(r1), cap(r1))
    r2 := append(s2, make([]int, 11)...)
    fmt.Printf("r2: len: %d, cap: %d\n", len(r2), cap(r2))
    fmt.Printf("注意:像r2 一次增加个数超过原容量的1倍,增加后结果比实际总长度预想的稍大一点 \n")
}
  • 第三种扩容策略-0.25倍增长
    1.原切片长度超过1024时,
    2.一次增加容量不是2倍而是0.25倍
    3.每次超过预定的都是0.25累乘
// @title  0.25倍的固定增长
// @description   2019/6/27   15:16  mick
// @param   void
// @return  void
// TODO  使用make函数创建一个1024个长度的切片
// TODO  打印此切片的容量
// TODO  使用append函数追加200个元素,并赋值给新切片
// TODO  打印此切片的长度与容量
// TODO  打印验证数值的正确性
func fixedIncrease(){
    s3 := make([]int, 1024)
    fmt.Printf("The capacity of s3: %d\n", cap(s3))
    r4 := append(s3, make([]int, 200)...)
    fmt.Printf("r4: len: %d, cap: %d\n", len(r4), cap(r4))
    fmt.Println(1024+1024*0.25)
}
image.png

课后练习

  • 第一题:为什么切片的值是一样的呢?
    var s []*int
    source := []int{1,2,3,4,5}
    for _,v := range source{
        s = append(s,&v)
    }
    fmt.Println(s)

高级讲解友情连接

相关文章

  • Go切片:二级跳实现优雅的“数组”

    切片是一种数据结构,便于使用和管理数据集合,切片是基于数组的概念构成,可以按需自动增长和缩小,切片底层也会是在连续...

  • Golang之数组和切片

    引用 数组、字符串和切片 Go数组中的索引问题 深入解析 Go 中 Slice 底层实现 Golang 入门 : ...

  • Go语言切片

    // //Go语言切片 // /* // go语言切片是对数组的抽象 // Go 数组的长度不可改变,在特定场景中...

  • 【Golang 基础】Go 语言的切片

    Go 语言的切片(示例代码) Slice 是一个通过指向数组底层,来进行变长数组的实现。 定义切片的格式:var ...

  • go 切片的底层实现

    go 数组切片的底层实现 go的切片也就是所谓的可变数组,当创建的时候,会发现大小只为24,原因就是他本质是一个结...

  • 第03天(复合类型)_03

    13_数组做函数参数.go 14_数组指针做函数参数.go 15_切片的长度和容量.go 16_切片的创建.go ...

  • 七、Go切片

    七、Go语言切片(Slice) Go 语言切片是对数组的抽象。 Go 数组的长度不可改变,在特定场景中这样的集合就...

  • go语言数组、切片、映射

    go的一些语法有点晦涩,这些很基础,做一下笔记 数组 数组声明 数组声明初始化 切片 切片的声明 切片的追加 切片...

  • Go 切片和数组

    数组 go创建数组和 c 语言类似,有如下两种方式: 切片 切片声明和数组类似,只是没有传递长度 创建二维切片,并...

  • golang系列教程

    Go包管理 Go开发工具 Go Doc 文档 Go 数组 Go 切片 Go Map Go 类型 Go 函数方法 G...

网友评论

    本文标题:Go切片:二级跳实现优雅的“数组”

    本文链接:https://www.haomeiwen.com/subject/nalsqctx.html