golang中数组与切片的区别详析

 更新时间:2022年11月30日 08:55:18   作者:周 -_-  
数组是固定长度,常量,切片长度是可以改变,所以是一个可变的数组,下面这篇文章主要给大家介绍了关于golang中数组与切片区别的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

一. Go 切片和 Go 数组定义

Go 切片:又称动态数组,它实际是基于数组类型做的一层封装。

Go 数组:数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从 0 开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数 len(array)获取其长度。

二.切片与数组的区别

Go 数组与像 C/C++等语言中数组略有不同:

1. Go 中的数组是值类型,换句话说,如果你将一个数组赋值给另外一个数组,那么,实际上就是将整个数组拷贝一份。因此,在 Go 中如果将数组作为函数的参数传递的话,那效率就肯定没有传递指针高了。

2. 数组的长度也是类型的一部分,这就说明[10]int和[20]int不是同一种数据类型。并且Go 语言中数组的长度是固定的,且不同长度的数组是不同类型,这样的限制带来不少局限性。

3. 而切片则不同,切片(slice)是一个拥有相同类型元素的可变长序列,可以方便地进行扩容和传递,实际使用时比数组更加灵活,这也正是切片存在的意义。而且切片是引用类型,因此在当传递切片时将引用同一指针,修改值将会影响其他的对象。

三. 切片使用

切片定义方式

  var a []int                    //nil切片,和nil相等,一般用来表示一个不存在的切片
   var b []int{}                //空切片,和nil不相等,一般用来表示一个空的集合
   var c []int{1, 2, 3}        //有3个元素的切片,len和cap都为3
   var d = c[:2]                //有2个元素的切片,len为2,cap为3
   var e = c[:2:cap(c)]        //有2个元素的切片,len为2,cap为3
   var f = c[:0]                //有0个元素的切片,len为0,cap为3
   var g = make([]int, 3)        //创建一个切片,len和cap均为3
   var h = make([]int, 3, 6)    //创建一个切片,len为3,cap为5
   var i = make([]int, 0, 3)    //创建一个切片,len为0,cap为3

从数组中切取切片

数组和切片是紧密相连的。切片可以用来访问数组的部分或全部元素,而这个数组称为切片的底层数组。切片的指针指向数组第一个可以从切片中访问的元素,这个元素并不一定是数组的第一个元素。

一个底层数组可以对应多个切片,这些切片可以引用数组的任何位置,彼此之前的元素可以重叠。

slice 操作符 s[i:j] 创建了一个新的 slice,这个新的 slice 引用了 s 中从 i 到 j-1 索引位置的所有元素。

如果表达式省略了 i,那么默认是s[0:j];如果省略了 j,默认是s[i:len(s)];

  //创建一个数组
  months := [...]string{1:"January", /*...*/, 12: "December"}
  Q2 := months[4:7]
  summer := months[6:9]
  fmt.Println(Q2)                //["April" "May" "June"]
  fmt.Println(summer)            //["June" "July" "August"]

月份名称字符串数组与其对应的两个元素重叠的 slice 图示

注意:切片与原数组或切片共享底层空间,修改切片会影响原数组或切片

迭代切片

切片可以用 range 迭代,但是要注意:如果只用一个值接收 range,则得到的只是切片的下标,用两个值接收 range,则得到的才是下标和对应的值。

  //使用一个值接收range, 则得到的是切片的下标
   for i := range months {
       fmt.Println(i)        //返回下标 0 1 ... 12
   }
   //使用两个值接收range,则得到的是下标和对应的值
   for i, v := range months {
       fmt.Println(i, v)     //返回下标0 1 ... 12 和 值 "" "January" ... "December"
   }

切片拷贝

使用copy内置函数拷贝两个切片时,会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片的最小值。

例如长度为 10 的切片拷贝到长度为 5 的切片时,将拷贝 5 个元素。也就是说,拷贝过程中不会发生扩容。

copy 函数有返回值,它返回实际上复制的元素个数,这个值就是两个 slice 长度的较小值。

切片扩容-append 函数

//通过append()函数可以在切片的尾部追加 N 个元素
  var a []int
  a = append(a, 1)                    // 追加一个元素
  a = append(a, 1, 2, 3)                // 追加多个元素
  a = append(a, []int{1, 2, 3}...)    // 追加一个切片,注意追加切片时后面要加...
//使用 append()函数也可以在切片头部添加元素
  a = append([]int{0}, a...)            // 在开头添加一个元素
  a = append([]int{1, 2, 3}, a...)    // 在开头添加一个切片

注:从头部添加元素会引起内存的重分配,导致已有元素全部复制一次。因此从头部添加元素的开销要比从尾部添加元素大很多

//通过 append()函数链式操作从中间插入元素
  a = append(a[:i], append([]int{x}, a[i:]...)...)        //在第i个位置上插入x
  a = append(a[:i], append([]int{1, 2, 3}, a[i:]...)...)    //在第i个位置上插入切片

使用链式操作在插入元素,在内层 append 函数中会创建一个临式切片,然后将a[i:]内容复制到新创建的临式切片中,再将临式切片追加至a[:i]中。

通过 append()和 copy()函数组合从中间插入元素

使用这种方式可以避免创建过程中间的临式切片,也可以做到从中间插入元素

  //中间插入一个元素
  a = append(a, 0)            //切片扩展一个空间
  copy(a[i+1:], a[i:])        //a[i:]向后移动一个位置
  a[i] = x                    //设置新添加的元素
  //中间插入多个元素
  a = append(a, x...)            //为x切片扩展足够的空间
  copy(a[i+len(x):], a[i:])    //a[i:]向后移动len(x)个位置
  copy(a[i:], x)                //复制新添加的切片

使用此方式虽然稍显复杂,但是可以减少创建中间临时切片的开销。

删除元素

很遗憾,Go 语言中并没有提供直接删除指定位置元素的方式。不过根据切片的性质,我们可以通过巧妙的拼接切片来达到删除指定数据的目的。

a = []int{1, 2, 3}
//删除尾部元素
a = a[:len(a) - 1]                //删除尾部一个元素
a = a[:len(a) - N]                //删除尾部N个元素
//删除头部元素
a = [1:]                        //删除开头1个元素
a = [N:]                        //删除开头N个元素
//删除中间元素
a = append(a[:i], a[i+1:]...)    //删除中间一个元素
a = append(a[:i], a[i+N:]...)    //删除中间N个元素

总结

到此这篇关于golang中数组与切片区别的文章就介绍到这了,更多相关go数组与切片区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • go循环依赖的最佳解决方案

    go循环依赖的最佳解决方案

      import cycle not allowed(循环依赖不被允许)相信作为每一个golang语言使用研发,都遇到过这个令人头痛的报错,循环依赖是指两个或多个模块之间互相依赖,形成了一个闭环的情况,本文会结合部分案例对解决方案进行讲解,需要的朋友可以参考下
    2023-10-10
  • go单例实现双重检测是否安全的示例代码

    go单例实现双重检测是否安全的示例代码

    这篇文章主要介绍了go单例实现双重检测是否安全,本文给大家分享双重检验示例代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-03-03
  • 一篇文章带你搞懂Go语言标准库Time

    一篇文章带你搞懂Go语言标准库Time

    在我们开发的过程中,每个项目都需要时间这一类的函数,此时对time这个包的研究深度就显得尤为重要,这篇文章主要给大家介绍了关于如何通过一篇文章带你搞懂Go语言标准库Time的相关资料,需要的朋友可以参考下
    2022-10-10
  • Go语言中reflect.DeepEqual函数的具体使用

    Go语言中reflect.DeepEqual函数的具体使用

    本文主要介绍了Go语言中reflect.DeepEqual函数的具体使用,reflect.DeepEqual()函数是用来比较两个值是否相等的方法,下面就来介绍一下,感兴趣的可以了解一下
    2024-08-08
  • Go 泛型和非泛型代码详解

    Go 泛型和非泛型代码详解

    Go 在 1.17 中支持泛型,但是默认未开启;1.18 中会正式支持泛型,下面文章内容小编将给大家讲解Go 语言中的泛型和非泛型并且附上代码详解,刚兴趣的小伙伴请参考下面文章的具体内容
    2021-10-10
  • 使用Go语言生成二维码并在命令行中输出

    使用Go语言生成二维码并在命令行中输出

    二维码(QR code)是一种矩阵条码的标准,广泛应用于商业、移动支付和数据存储等领域,在开发过程中,我们可能需要在命令行中显示二维码,这可以帮助我们快速生成和分享二维码信息,本文将介绍如何使用Go语言生成二维码并在命令行中输出,需要的朋友可以参考下
    2023-11-11
  • go GCM gin中间件的加密解密文件流处理

    go GCM gin中间件的加密解密文件流处理

    这篇文章主要介绍了go语言 GCM加密解密,gin中间件的加密解密及文件流处理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪<BR>
    2022-05-05
  • 基于go微服务效率工具goctl深度解析

    基于go微服务效率工具goctl深度解析

    这篇文章主要为大家介绍了基于go微服务效率工具goctl深度解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 使用go连接clickhouse方式

    使用go连接clickhouse方式

    这篇文章主要介绍了使用go连接clickhouse方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • GO中的时间操作总结(time&dateparse)

    GO中的时间操作总结(time&dateparse)

    日常开发过程中,对于时间的操作可谓是无处不在,但是想实现时间自由还是不简单的,多种时间格式容易混淆,本文为大家整理了一下GO中的时间操作,有需要的可以参考下
    2023-09-09

最新评论