一、前言

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

  

二、数组

1.简介

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

  数组的声明语法为:

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

  语法说明如下所示:

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

2.初始化

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

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

3.遍历数组

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

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

三、切片

1.简介

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

2.区别

  切片和数组有如下区别:

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

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

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

 package main

 import (
"fmt"
) func change(a []int, s []int) {
a[] +=
s[] +=
s = append(s, )
} func main() {
arr := []int{, , }
sli := []int{, , }
change(arr, sli)
fmt.Println(arr) // [2 4 6]
fmt.Println(sli) // [4 5 7]
}

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

3.初始化

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

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

4.追加元素

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

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

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

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

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

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

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

 var y []int
for i := ; i < ; i++ {
y = append(y, i)
fmt.Printf("%d cap=%d %v\n", i, cap(y), y)
}

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

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 中纠结深浅拷贝真的很幸福呢!只不过拷贝的时候需要确保目的切片有足够的容量,否则会拷贝。示例如下:

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

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

【Go入门学习】理解区分数组和切片的更多相关文章

  1. go 学习笔记之数组还是切片都没什么不一样

    上篇文章中详细介绍了 Go 的基础语言,指出了 Go 和其他主流的编程语言的差异性,比较侧重于语法细节,相信只要稍加记忆就能轻松从已有的编程语言切换到 Go 语言的编程习惯中,尽管这种切换可能并不是特 ...

  2. Scala入门学习笔记三--数组使用

    前言 本篇主要讲Scala的Array.BufferArray.List,更多教程请参考:Scala教程 本篇知识点概括 若长度固定则使用Array,若长度可能有 变化则使用ArrayBuffer 提 ...

  3. [Golang学习笔记] 07 数组和切片

    01-06回顾: Go语言开发环境配置, 常用源码文件写法, 程序实体(尤其是变量)及其相关各种概念和编程技巧: 类型推断,变量重声明,可重名变量,类型推断,类型转换,别名类型和潜在类型 数组: 数组 ...

  4. 深入学习golang(1)—数组与切片

    数据(array)与切片(slice) 数组声明: ArrayType   = "[" ArrayLength "]" ElementType . 例如: va ...

  5. Go语言学习笔记(4)——数组和切片

    1 数组的特点: 长度固定.元素数据类型相同.下标从0开始 1.1 声明和初始化: var array_name [size] type         var arr1 [10] float32   ...

  6. Egg入门学习(二)---理解service作用

    在上一篇文章 Egg入门学习一 中,我们简单的了解了Egg是什么东西,且能做什么,这篇文章我们首先来看看官网对Egg的整个框架的约定如下,及约定对应的目录是做什么的,来有个简单的理解,注意:我也是按照 ...

  7. Go语言入门——数组、切片和映射(下)

    上篇主要介绍了Go语言里面常见的复合数据类型的声明和初始化. 这篇主要针对数组.切片和映射这些复合数据类型从其他几个方面介绍比较下. 1.遍历 不管是数组.切片还是映射结构,都是一种集合类型,要从这些 ...

  8. Go语言入门——数组、切片和映射

    按照以往开一些专题的风格,第一篇一般都是“从HelloWorld开始” 但是对于Go,思来想去,感觉真的从“HelloWorld”说起,压根撑不住一篇的篇幅,因为Go的HelloWorld太简单了. ...

  9. opengl入门学习

    OpenGL入门学习 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640 ...

随机推荐

  1. 解决 scp 和rsync 同步失败【rsync error: unexplained error (code 255) at io.c(226) [sender=3.1.2]】

    解决 scp 和rsync 同步失败 报错信息截图: 解决问题的步骤: 1.检查对方的scp和rsync 是否能使用. rsync 在使用的时候,需要客户端和服务端都有rsync工具.scp 和 rs ...

  2. 面试总问的jvm调优到底是要干什么?

    1. 压力测试的理解,xxx的性能10w/s,对你有意义么? 没有那家卖瓜的会说自己家的不甜,同样,没有哪个开源项目愿意告诉你在对它条件最苛刻的时候压力情况是多少,一般官网号称给你看的性能指标都是在最 ...

  3. Typings移除Deprecated Warning

    使用TypeScript进行开发中,经常遇到如下的Deprecated Warning.虽然没有实际影响,但看多了,确实挺烦. 要想消除这些Warning,需要以下几个步骤: 步骤一,确认Warnin ...

  4. 『嗨威说』算法设计与分析 - 贪心算法思想小结(HDU 2088 Box of Bricks)

    本文索引目录: 一.贪心算法的基本思想以及个人理解 二.汽车加油问题的贪心选择性质 三.一道贪心算法题点拨升华贪心思想 四.结对编程情况 一.贪心算法的基本思想以及个人理解: 1.1 基本概念: 首先 ...

  5. spark基于yarn的两种提交模式

    一.spark的三种提交模式 1.第一种,Spark内核架构,即standalone模式,基于Spark自己的Master-Worker集群. 2.第二种,基于YARN的yarn-cluster模式. ...

  6. Maven系列第8篇:你的maven项目构建太慢了,我实在看不下去,带你一起磨刀!!多数使用maven的人都经常想要的一种功能,但是大多数人都不知道如何使用!!!

    maven系列目标:从入门开始开始掌握一个高级开发所需要的maven技能. 这是maven系列第8篇. 整个maven系列的内容前后是有依赖的,如果之前没有接触过maven,建议从第一篇看起,本文尾部 ...

  7. nyoj 67-三角形面积 (海伦公式, 叉积)

    67-三角形面积 内存限制:64MB 时间限制:3000ms 特判: No 通过数:8 提交数:13 难度:2 题目描述: 给你三个点,表示一个三角形的三个顶点,现你的任务是求出该三角形的面积 输入描 ...

  8. Robot Framework自动化测试环境搭建

    robotFramework是一个通用的自动化测试框架来进行验收测试和验收测试驱动开发模式,它具有易于使用的表格的测试数据和关键字测试驱动方法,其测试功能可通过实现与python或java的测试库进行 ...

  9. B0宏

    在编译android平台用的ffmpeg时,抛出这样一个错误: 这句代码怎么看都找出有毛病,为什么B0会报错? 翻看aaccoder.c,也没有发现问题.为什么B0就成了一个常量数字,这里只有一个可能 ...

  10. python3 之 函数传参

    一.可变对象与不可变对象 在 python 中,strings, tuples, 和 numbers 是不可更改的对象,而 list,dict 等则是可以修改的对象. 不可变类型:变量赋值 a=5 后 ...