一、前言

  学过 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. Spring 框架常用语法进行总结

    Spring 框架常用语法进行总结: spring框架的二大主要的功能就是IOC和AOP. IOC: 控制反转(依赖注入) AOP: 面向切面编程 学习spring最好的方法就是去看官网,里面有详细的 ...

  2. opencv 5 图像转换(2 霍夫变换)

    霍夫线变换 标准霍夫变换和多尺度霍夫变换(HoughLines()函数) 实例: #include <opencv2/opencv.hpp> #include <opencv2/im ...

  3. nexus https proxy

  4. 使用Docker搭建maven私服 及常规使用方法

    安装-登录-配置 下载镜像 docker pull sonatype/nexus3 运行 docker run -d -p 9998:8081 --name nexus --restart=alway ...

  5. HDFS之NameNode

    NameNode&Secondary NameNode工作机制 1)第一阶段:namenode启动 (1)第一次启动namenode格式化后,创建fsimage和edits文件.如果不是第一次 ...

  6. htm&css 颜色的浮动

    目标样式 目标分析: 由图可知,整个色板可以分为三块 可以理解为一个大的盒子包含了三个小的盒子,定义如下 LEFT的定义方式 TOP的定义方式 class为bottom的块最为复杂,但分析方法也是异曲 ...

  7. Windows Server 2008 服务器重启后卡死在Windows Update 页面问题处理

    Windows Update 服务器 服务器是联想RD640 操作系统Windows Server 2008 R2 Enterprise版 补丁版本是SP1 远程windows服务器时,一直处于远程建 ...

  8. CSS中如果实现元素浮动,看这篇文章就足够了

    浮动基本介绍 在标准文档流中元素分为2种,块级元素和行内元素,如果想让一些元素既要有块级元素的特点也同时保留行内元素特点,只能让这些元素脱离标准文档流即可. 浮动可以让元素脱离标准文档流,可以实现让多 ...

  9. 音视频入门-14-JPEG文件格式详解

    * 音视频入门文章目录 * JPEG 文件格式解析 JPEG 文件使用的数据存储方式有多种.最常用的格式称为 JPEG 文件交换格式(JPEG File Interchange Format,JFIF ...

  10. Ansible 介绍和安装

    目录 Ansible 介绍 环境准备 Ansible安装 配置秘钥管理 配置Inventory文件 简单测试连通性 Ansible 介绍 运维工具分类: agent: puppet, func 这类都 ...