• 【Golang之路】——slice总结


    引言

    Go中的切片(slice)数据结构对比java来说,还是一个稍微新的概念,但是数组对于我们来说确实一个非常熟悉的数据结构,但是由于slice在go编程中使用的非常频繁,所以今天我们来详细的总结一下这个数据结构。

    slice是什么

    切片和数组一样,也是可以容纳若干类型相同的元素的容器。我们来看下,go语言中slice中的定义:

    1. type slice struct {
    2. array unsafe.Pointer
    3. len int
    4. cap int
    5. }
    6. // unsafe.Pointer 类型如下
    7. // Pointer represents a pointer to an arbitrary type. There are four special operations
    8. // available for type Pointer that are not available for other types:
    9. // - A pointer value of any type can be converted to a Pointer.
    10. // - A Pointer can be converted to a pointer value of any type.
    11. // - A uintptr can be converted to a Pointer.
    12. // - A Pointer can be converted to a uintptr.
    13. // Pointer therefore allows a program to defeat the type system and read and write
    14. // arbitrary memory. It should be used with extreme care.
    15. type Pointer *ArbitraryType

    切片 GO 的一种数据类型 , 是对数组的一个连续片段的引用

    切片的底层结构是一个结构体,对应有三个参数

     array:是一个 unsafe.Pointer 指针,指向一个具体的底层数组

    Len:切片的长度

    Cap:切片的容量

    从上面的图中,我们看到切片的底层也是依赖数组进行数据存储,那么切片和数组🈶有什么不同呢?

    切换与数组的不同

    1、数组是值传递,而切片是引用传递

    在go语言中,数组的传递是通过值拷贝的方式,传递切片是通过引用的方式,这里说的引用,指的是 切片数据结构中 array 字段,其余字段默认是值传递。

    2、数组的长度固定

    数组是相同类型的,一组内存空间连续的数据,他的每一个元素的数据类型都是一样的,且数组的长度一开始就确定好了,且不能做变动,而切片的长度是可以自动扩容的。

    3、切片是一个结构体,包含三个属性

    切片是引用类型,长度不固定,可扩展,在日常编码中使用的非常频繁。

    切片的创建方式

    1、使用var 创建一个空切片

    var s []string

    2、使用make创建一个指定长度和容量的切片

    ints := make([]int,4, 8)

    当我们使用make方恨少创建的时候,系统会自动给指定的容量赋值零值

    1. for _, i := range ints {
    2. fmt.Printf("%v", i)
    3. }
    4. 结果:0000

    这里需要我们注意,当我们遍历切片的时候,是 切片的长度len,而不是切片的容量cap。

    3、使用数组赋值的方式创建切片

    1. var num =[...]int{1,2,3,4,5,6,7,8}
    2. myslice:=num[4:6]
    3. fmt.Printf("长度:%d;容量:%d \n",len(myslice),cap(myslice))
    4. myslice = myslice[:cap(myslice)]
    5. fmt.Printf("新长度:%d;新容量:%d \n",len(myslice),cap(myslice))
    6. v:=myslice[3]
    7. fmt.Printf("值:%d",v)
    8. 结果:
    9. 长度:2;容量:4
    10. 新长度:4;新容量:4
    11. 值:8

     

     

    从上面的结果中我们,可能存在下面几个问题:

    1、容量为什么是4?

    上述代码 arr[4:6] 指的是将数组的下标为 4 开始的位置,下标为 6 的为结束位置,这里是不包含 6 自己的

    根据 GO 中切片的原理,用数组复制给到切片的时候,若复制的数组元素后面还有内容的话,则后面的内容都作为切片的预留内存

    即得到上述的结果,len == 2, cap == 4

    不过这里还是要注意,切片元素对应的地址,还是这个数组元素对应的地址,使用的时候需要小心。

    2、当我们重新定义一个新的切片的时候,为什么是8?

    这是因为我们  myslice = myslice[:cap(myslice)],重新生成一个新的数组的时候,结构如下:

    slice扩容原理

    1. myslice:=make([]int,4,4)
    2. fmt.Printf("长度:%d;容量:%d \n",len(myslice),cap(myslice))
    3. myslice = append(myslice,1)
    4. fmt.Printf("新长度:%d;新容量:%d \n",len(myslice),cap(myslice))
    5. 结果:
    6. 长度:4;容量:4
    7. 新长度:5;新容量:8

    从上面的结果中,可以直观的感受到,当我们想切片中增加给元素的时候,切片的容量从原理的4扩容到8.

    我们来看看切片扩容的规则是这样的:

    如果原来的切片容量小于 1024
    那么新的切片容量就会扩展成原来的 2 倍

    如果原切片容量大于等于 1024
    那么新的切片容量就会扩展成为原来的 1.25 倍

    我们再来梳理一下上述扩容原理的步骤是咋弄的

    上述切片扩容,大致分为如下 2 种情况:

    1、添加的元素,加入到切片中,若原切片容量够
            那么就直接添加元素,且切片的 len ++ ,此处的添加可不是直接赋值,可是使用 append 函数的方式。

    2、若原切片容量不够,则先将切片扩容,再将原切片数据追加到新的切片中

     空切片和nil切片

    1. func main(){
    2. // 是一个空对象
    3. var mys1 []int
    4. // 是一个对象,对象里面是一个切片,这个切片没有元素
    5. var mys2 = []int{}
    6. json1, _ := json.Marshal(mys1)
    7. json2, _ := json.Marshal(mys2)
    8. fmt.Println(string(json1))
    9. fmt.Println(string(json2))
    10. }
    11. 结果:
    12. null
    13. []

     原因是这样的:

    • mys1 是一个空对象
    • mys2 不是一个空对象,是一个正常的对象,但是对象里面的为空

    总结

    切片在go语言的日常编程中使用的非常频繁,如果我们能理解切片的一些底层原理和正确的使用方式,可以提高我们程序的运行效率。

  • 相关阅读:
    Animate动画
    老张的思考
    30岁后再转行程序员,能行吗?
    Spring常见问题解决 - 自定义ApplicationEnvironmentPreparedEvent监听器失效了?
    简析CloudCompare文件夹之间的关系
    Shiro第一个程序官方快速入门程序Qucickstart详解教程
    解决pycococreatortools安装问题
    Python学习笔记--构造(`__new__`)和初始化(`__init__`)
    TCP协议的秘密武器:流量控制与拥塞控制
    Windows11安装Maven,配置环境变量
  • 原文地址:https://blog.csdn.net/u013045437/article/details/126141782