一、前言

  学过 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. 微擎修改 icon.jpg 后项目主页未变

    微擎修改 icon.jpg 后项目主页Logo未变 产生原因: 设置了自定义图标,但系统未找到该图标,就选择使用默认的起始图标 解决办法: 在项目根目录位置上传一个图标名为 icon-custom.j ...

  2. XjhDemo 插入数据

    create database XjhDemo gouse XjhDemogo --商品类别Create table Sort( SortId int primary key identity(1,1 ...

  3. 函数的prototype

    1.函数的prototype属性 每一个函数都有一个prototype属性,默认指向object空对象(原型对象),每一个原型对象都有一个constructor属性,指向函数对象 2.给原型对象添加属 ...

  4. 装饰者模式学习:模拟咖啡馆的点单系统来剖析装饰者模式的使用 + 装饰者模式在java I/O 中的应用

    通过模拟咖啡馆的点单系统来剖析装饰者模式的使用 参考:https://blog.csdn.net/gududedabai/article/details/81989196 一).传统的点单系统构建,每 ...

  5. elasticsearch安装踩坑记

    ES的安装与启动时问题解决 环境: ​ 系统环境:CentOS7 ​ JDK:jdk-8u131-linux-x64.tar.gz 不可以通过Root用户来启动ES 通过上面的错误提示可以看出,can ...

  6. Redshitf Install

    创建VPC 和 子网和internet网关(子网需开启自动分配公网IP,VPN 需添加到internet网关的路由) 创建安全组: 创建cluster subnet Group; 创建redshift ...

  7. toString() 方法的参数

    除开null 和 undefined之外所有的数据类型都是拥有toString方法的. 通常情况下我们使用toString()方法的时候都是不用传递参数的,但是Number类型的toString方法是 ...

  8. 2019-9-10:渗透测试,基础学习,sql注入笔记

    sql注入1,万能密码,自己写的网站,找到登录窗口,必须和数据库交互,往里插入构造的恶意代码,最后可以直接登录进去,不需要账号和密码,输入的恶意代码成为万能密码,后端拼接的sql语句,SELECT * ...

  9. mac系统下docker安装配置mysql详细步骤

    上文介绍了MacOS安装Docker傻瓜式教程,安装好后第一件事就决定把本地数据库迁移过来,那么首先就得安装mysql,下面就开始我们的安装之旅吧. 一.docker配置镜像加速器 我们使用docke ...

  10. 02-tornado学习笔记-环境配置

    Ubuntu16.04开发环境 1.ubuntu默认root用户没有激活,激活root用户,就要为root用户创建密码   $sudo passwd root   2.修改主机名   $vi /etc ...