• 【Go入门学习】理解区分数组和切片


    一、前言

      学过 Go 的都知道在 Go 语言中有四种复合数据类型:数组、切片(Slice)、哈希表(Map)和结构体(Struct),而很多 Go 初学者也很容易把数组和切片弄混淆,所以要怎么把这两个数据类型分清楚呢?

       

    二、数组

    1.简介

      数组是聚合类型,是一组同类型数据的集合,通过从0开始的下标索引访问元素值。在 Go 语言中,数组是值类型,这就意味着当你将一个数组赋值给另一个数组的时候,实际上是将这个数组拷贝了一份。

      数组的声明语法为:

    var 数组变量名 [元素数量]Type

      语法说明如下所示:

    • 数组变量名:数组声明及使用时的变量名;
    • 元素数量:数组的元素数量,可以是一个表达式,但最终计算的结果必须是整型数值;
    • Type:可以是任意基本类型,包括数组本身,类型为数组本身时,可以实现多维数组。

    2.初始化

      默认情况下,数组的每个元素都被初始化为元素类型对应的零值,对于数字类型来说就是0。在数组字面值中,如果在数组的长度位置出现的是“...”省略号,则表示数组的长度是根据初始化值的个数来计算。示例如下:

    1 var a [3]int
    2 a[0] = 1
    3 b := [2]int{1, 2}
    4 c := [...]int{3, 5, 7}
    5 fmt.Println(a) // [1 0 0]
    6 fmt.Println(b) // [1 2]
    7 fmt.Println(c) // [3 5 7]

    3.遍历数组

      数组通过下标访问元素,可修改其元素值。数组的遍历通过 for 循环实现:

    1 arr := [3]int{2, 4, 6}
    2 for i := 0; i < 3; i++ {
    3     fmt.Printf("%d ", arr[i])
    4 } // 2 4 6 
    5 fmt.Println()
    6 for _, v := range arr {
    7     fmt.Printf("%d ", v)
    8 } // 2 4 6 

    三、切片

    1.简介

      数组的长度不可改变,在一定场合下就不太适用了,Go 语言则提供了一种可以动态扩容的数据类型--切片(Slice)。一个切片类型通常会写作 []T,其中 T 代表切片中元素的数据类型,切片的语法和数组类似,只是没有固定长度。  

    2.区别

      切片和数组有如下区别:

      1)和数组相比,切片除了有长度(len),还有容量(cap),容量指切片当前可容纳元素的最大数量。

      2)数组是值类型,切片是引用类型。

      值类型和引用类型有什么区别呢?在传递参数时,如果是值类型,对参数修改不会对原来的变量产生影响,但若是引用传递,对参数的修改也会影响到原始数据。示例如下:

     1 package main
     2 
     3 import (
     4     "fmt"
     5 )
     6 
     7 func change(a [3]int, s []int) {
     8     a[0] += 1
     9     s[0] += 1
    10     s = append(s, 9)
    11 }
    12 
    13 func main() {
    14     arr := [3]int{2, 4, 6}
    15     sli := []int{3, 5, 7}
    16     change(arr, sli)
    17     fmt.Println(arr) // [2 4 6]
    18     fmt.Println(sli) // [4 5 7]
    19 }

      在示例中,分别对数组 arr 和切片 sli 的第一个元素进行了+1操作,但从打印结果可以看出来只有切片的数据被修改了,而对数组的修改并没有改变原始数据。那为什么最后 sli 的结果不是 [4 5 7 9]呢?这是因为 append() 实际上是将切片 sli 复制了一份然后赋值给了 s,已经是一份新的数据了,也就不会对 sli 产生影响了。

    3.初始化

      切片的初始化可以通过数组来实现,也可以通过内置函数 make() 来实现,在使用 make() 方法时还可以设置切片的容量,在追加元素时,若切片的容量不足,则会按切片的长度的二倍进行扩容。示例如下:

    1 arr := [5]int{1, 2, 3, 4, 5}
    2 s1 := arr[2:]
    3 fmt.Println(s1) // [3 4 5]
    4 s2 := arr[:]
    5 fmt.Println(s2) // [1 2 3 4 5]
    6 s3 := make([]int, 3)
    7 s3[0], s3[1], s3[2] = 2, 4, 6
    8 fmt.Println(s3) // [2 4 6]

    4.追加元素

      在 Go 语言中有一个内置函数 append(),查看源码发现它是这么定义的:

    func append(slice []Type, elems ...Type) []Type

      内置的 append() 函数用于向 slice 追加元素,示例为:

    1 arr := [5]int{1, 2, 3, 4, 5}
    2 var sli []int
    3 for _, v := range arr {
    4     sli = append(sli, v)
    5 }
    6 fmt.Println(sli) // [1 2 3 4 5]

      细心的人会发现源码中写的是 elems,这是不是就意味着可以一次添加多个元素呢?试一试:

    1 var sli []int
    2 sli = append(sli, 1, 2, 3)
    3 fmt.Println(sli) // [1 2 3]

      例子很简单,append() 使用起来也很方便,但问题是如果要添加的元素数量超过了切片的容量,又会发生什么情况呢?看下面的例子:

    1 var y []int
    2 for i := 0; i < 10; i++ {
    3     y = append(y, i)
    4     fmt.Printf("%d cap=%d %v
    ", i, cap(y), y)
    5 }

      这几行代码的运行结果为:

    0 cap=1 [0]
    1 cap=2 [0 1]
    2 cap=4 [0 1 2]
    3 cap=4 [0 1 2 3]
    4 cap=8 [0 1 2 3 4]
    5 cap=8 [0 1 2 3 4 5]
    6 cap=8 [0 1 2 3 4 5 6]
    7 cap=8 [0 1 2 3 4 5 6 7]
    8 cap=16 [0 1 2 3 4 5 6 7 8]
    9 cap=16 [0 1 2 3 4 5 6 7 8 9]

      可以发现切片的容量从1慢慢增加为2、4、8、16,也就是说在使用 append 将元素添加至切片时,如果超出了容量,将会返回一个容量二倍与当前切片的切片

    5.切片拷贝

      在 Go 语言中,切片的拷贝使用内置函数 copy() 来实现,可以放心的是,切片拷贝是深拷贝,不用像 Python 中纠结深浅拷贝真的很幸福呢!只不过拷贝的时候需要确保目的切片有足够的容量,否则会拷贝。示例如下:

    1 sli := []int{3, 5, 7}
    2 res := make([]int, 5)
    3 copy(res, sli)
    4 fmt.Println(res) // [3 5 7 0 0]
    5 fmt.Println(&sli[0], &res[0]) // 0xc000012340 0xc00000c3c0
    6 var s []int
    7 copy(s, sli)
    8 fmt.Println(s) // []

      这里 s 打印出来是空的,是由于 s 在初始化的时候没有分配内存空间,copy() 也不会为 s 分配空间,所以 sli 中的元素也就无法拷贝到 s 中了。

  • 相关阅读:
    使用MingGW-w64 Build Script 3.6.7搭建ffmpeg编译环境
    ffmpeg精简编译
    CListCtrl死锁的问题
    VC程序禁用提示框
    rtmp协议分析
    [置顶] zabbix发送告警
    [置顶] 个人微信号发送zabbix告警信息
    [置顶] 一个简单好用的zabbix告警信息发送工具
    [置顶] zabbix告警信息-lykchat信息发送系统
    模拟登陆web微信的流程和参数细节
  • 原文地址:https://www.cnblogs.com/TM0831/p/12043614.html
Copyright © 2020-2023  润新知