《Go语言系列文章》

  1. Go语言系列(一)之Go的安装和使用
  2. Go语言系列(二)之基础语法总结

1. 数组

数组用于存储若干个相同类型的变量的集合。数组中每个变量称为数组的元素,每个元素都有一个数字编号——数组下标,该下标从0开始,用于区别各个元素。数组中可容纳的元素个数称为数组的长度

1.1. 声明

Go语言中数组的声明方式:

var arr_name [length]type

var:不必多说,声明变量时都会用到该关键字。

arr_name:数组名称,本质是个变量

length:数组的长度

type:数组的类型

[]:通过它来进行对数组元素的读取、赋值

下面是一个例子:

package main

import "fmt"

func main() {
var a [2]string //声明一个长度为2的string数组
a[0] = "我是" //赋值
a[1] = "行小观"
fmt.Println(a[0], a[1]) //获取元素
fmt.Println(a)
}

1.2. 初始化

《Go语言系列(二)之基础语法总结》这篇文章中提过:若我们在声明变量时,不给变量赋初始值,则这些变量会被赋予“零值”。

数组中也是这样,如果不初始化,则数组中的所有元素值都为“零值”。如下例:

package main

import "fmt"

func main() {
var a [3]int
var b [3]string
var c [3]bool fmt.Println(a) //[0 0 0]
fmt.Println(b) //[ ]
fmt.Println(c) //[false false false]
}

对数组元素进行初始化:

package main

import "fmt"

func main() {
var a = [5]int {1, 2, 3}
fmt.Println(a) //[1 2 3 0 0]
}

只初始化了部分元素,剩余的仍是零值。

如果我们在声明数组时同时初始化了,可以使用...而不指定数组的长度,Go会自动计算数组长度:

var a = [...]int {1, 2, 3} //初始化,数组长度为3

1.3. 短变量方式声明

当然,我们可以使用短变量声明的方式声明数组。注意:使用该方式就必须在声明的时候同时初始化

如果你只是想使用这种方式来声明一个数组,但并不初始化,可以这样做,但是必须带上{}

package main

import "fmt"

func main() {
a := [5]int {1, 2, 3} //初始化
b := [3]int {}
c := [...]int {1, 2, 3} fmt.Println(a) //[1 2 3 0 0]
fmt.Println(b) //[0 0 0]
fmt.Println(c) //[1 2 3]
}

1.4. 特殊之处

注意:在Go语言中,数组的长度是其类型的一部分。 所以Go中的数组不能改变长度。

怎么理解?下面声明了两个数组:

var a [4]int //将变量a声明为拥有4个整数的数组

var b [5]int //将变量b声明为拥有5个整数的数组

变量ab 的类型分别为[4]int[5]int,是不同的类型。

1.5. 二维数组

二维数组当中的元素仍是数组:

var ab = [2][4]int {[4]int {1, 2, 3, 4}, [4]int {4, 5, 6, 7}}

ab := [2][4]int {[4]int {1, 2, 3, 4}, [4]int {4, 5, 6, 7}}

可以省去数组元素的类型:

var ab = [2][4]int {{1, 2, 3, 4}, {4, 5, 6, 7}}

ab := [2][4]int {{1, 2, 3, 4}, {4, 5, 6, 7}}

1.6. 遍历数组

(一)使用数组长度

可以使用len(slice)函数获取数组长度,然后遍历。

arr := [5]string {"a", "b", "c", "d", "e"}

bc := [2][4]int {
{1, 2, 3, 4},
{5, 6, 7, 8},
} for i := 0; i < len(arr); i++ {//遍历一维数组
fmt.Println(arr[i])
} for i := 0; i < len(bc); i++ {//遍历二维数组的元素
fmt.Println(bc[i])
} for i := 0; i < len(bc); i++ {//遍历二维数组的元素的元素
for j := 0; j < len(bc[0]); j++ {
fmt.Println(bc[i][j])
}
}

(二)使用range关键字

range关键字用于for循环中遍历数组时,每次迭代都会返回两个值,第一个值为当前元素的下标,第二值为该下标所对应的元素值。如果这两个值的其中一个你不需要,只需使用下划线_代替即可。

arr := [5]string {"a", "b", "c", "d", "e"}

bc := [2][4]int {
{1, 2, 3, 4},
{5, 6, 7, 8},
} for i, v := range arr {//遍历一维数组
fmt.Println(i, v)
} for i := range arr {//遍历时只获取下标
fmt.Println(i)
} for _, v := range arr{//遍历时只获取元素值
fmt.Println(v)
} for _, v := range bc {//遍历二维数组
for _, w := range v{
fmt.Println(w)
}
}

2. 切片(slice)

前面提到:Go中的数组的长度是固定的。这样就会在实际应用中带来不方便,因为很多时候在声明数组前并不明确该数组要存储多少个元素。声明太多,浪费;声明太少,不够用。

而切片就为我们提供了“动态数组”。

2.1. 使用

声明切片和声明数组类似,但是不指定长度

var sli_name []type

比如,声明一个int类型、名为a的切片:

var a []int

可以在声明它的时候直接初始化:

var a = []int {1, 2, 3, 4}

当然,也可以使用短变量的方式声明:

a := []int {1, 2, 3, 4}

可以从一个已有的数组或者已有的切片中获取切片。

获取切片的方式是通过两个下标来获取,即开始下标(startIndex)和结束下标(endIndex),二者以冒号分隔。包括startIndex,不包括endIndex

a[startIndex : endIndex]

下面是一个例子:

a := [5]string {"a", "b", "c", "d", "e"} //数组
b := []int {1, 2, 3, 4} //切片 sliA := a[2:4]
sliB := b[1:3] fmt.Println(sliA) //[c d]
fmt.Println(sliB) //[2 3]

2.2. 切片与数组

前面提到:切片为我们提供了“动态数组”。但该“动态数组”并不是真正意义上的能扩展长度的动态数组。

切片并不存储任何数据,它只是一个引用类型,切片总是指向一个底层的数组,描述这个底层数组的一段。

所以我们在声明数组时需要指定长度,而声明切片时不需要:

var arr = [4]int {1, 2, 3, 4} //声明数组

var slice = []int {1, 2, 3, 4} //声明切片

由于切片的底层引用的是数组,所以更改切片中的元素会修改其底层数组中对应的元素,如果还有其他切片也引用了该底层数组,那么这些切片也能观测到这些修改。如图:

下面是一个例子:

package main

import "fmt"

func main() {
array := [5]string {"aa", "bb", "cc", "dd", "ee"} //数组
fmt.Println(array) //[aa bb cc dd ee] slice1 := array[0:2] //切片1
slice2 := array[1:3] //切片2
slice3 := array[2:5] //切片3 fmt.Println(slice1) //[aa bb]
fmt.Println(slice2) //[bb cc]
fmt.Println(slice3) //[cc dd ee] slice1[0] = "xx" //修改切片1中的值
slice2[1] = "yy" //修改切片2中的值
slice3[2] = "zz" ////修改切片3中的值 fmt.Println(array) //[xx bb yy dd zz]
fmt.Println(slice1) //[xx bb]
fmt.Println(slice2) //[bb yy]
fmt.Println(slice3) //[yy dd zz]
}

2.3. 切片的相关操作

(一)长度

切片的长度指切片所包含的元素个数。通过函数len(s)获取切片s的长度。

(二)容量

切片的容量指切片的第一个元素到其底层数组的最后一个元素的个数。通过函数cap(s)获取切片s的容量。

下面是一个例子:

package main

import "fmt"

func main() {
arr := [10]string {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
s := arr[2:5] //创建切片s fmt.Println(arr) //[a b c d e f g h i j]
fmt.Println(s) //[c d e] fmt.Println(len(s)) //3
fmt.Println(cap(s)) //8
}

下面是长度和容量的示意图:

有了容量这个概念,我们就可以通过重新切片来改变切片的长度:

package main

import "fmt"

func main() {
arr := [10]string {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}
s := arr[2:5]
fmt.Printf("s的长度为%d,s的容量为%d\n", len(s), cap(s))
s = s[2:8]
fmt.Printf("s的长度为%d,s的容量为%d\n", len(s), cap(s))
s = s[0:2]
fmt.Printf("s的长度为%d,s的容量为%d\n", len(s), cap(s))
}

(三)追加元素

使用func append(slice []Type, elems ...Type) []Type可以向切片slice的末尾追加类型为Type的元素elems

该函数的结果是一个包含原切片所有元素加上新添加元素的切片。由于改变切片内容了,所以底层数组也会被改变。

package main

import "fmt"

func main() {
s := []string {"a", "b", "c", "d"}
s = append(s, "e") //追加1个
s = append(s, "f", "g", "h") //追加3个
fmt.Println(s)
}

当切片中容量已经用完时(len(s) == cap(s)),也即底层数组容纳不了追加的元素时,Go会分配一个更大的底层数组,返回的切片指向这个新分配的数组,原数组的内容不变。

package main

import "fmt"

func main() {
arr := [5]string {"a", "b", "c", "d", "e"}
slice1 := arr[0:2]
fmt.Println(slice1) //[a b]
//追加3个元素,slice1的容量已满
slice1 = append(slice1, "1", "2", "3")
fmt.Println(slice1) //[a b 1 2 3]
//底层数组跟着改变
fmt.Println(arr) //[a b 1 2 3]
//继续追加
slice1 = append(slice1, "4", "5")
//指向新的底层数组
fmt.Println(slice1) //[a b 1 2 3 4 5]
//原底层数组不变
fmt.Println(arr) //[a b 1 2 3]
}

(四)复制切片

func copy(dst []Type, src []Type) int

dst是目标切片,src是源切片,该函数会将src中的元素复制到dst中,并返回复制的元素个数(该返回值是两个切片长度中的小值)

package main

import "fmt"

func main() {
slice1 := []string {"a", "b"}
slice2 := []string {"1", "2", "3"}
length := copy(slice2, slice1)
//length := copy(slice1, slice2)
fmt.Println(length)
fmt.Println(slice1)
fmt.Println(slice2)
}

(五)切片的默认行为

切片的默认开始下标是0,默认结束下标是切片的长度。

对于数组:

var a [10]int

下面几个切片是等价的:

a[0:10]
a[:10]
a[0:]
a[:]

2.4. 特殊切片

(一)nil切片

切片的零值是 nil,当声明一个切片,但不出初始化它,该切片便为nil切片。nil切片的长度和容量为0且没有底层数组。

func main() {
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
fmt.Println("s切片是nil切片")
}
}

(二)切片的切片

切片中的元素可以是切片

package main

import "fmt"

func main() {
ss := [][]int {
[]int {1, 2, 3}, //切片元素的类型可以省去
[]int {4, 5, 6},
[]int {7, 8, 9},
} for i := 0; i < len(ss); i++ {
fmt.Println(ss[i])
}
}

2.5. 使用make函数创建切片

使用make函数可以在创建切片时指定长度和容量。make函数会分配一个元素为零值的数组并返回一个引用了它的切片

该函数接受三个参数,分别用来指定切片的类型、长度、容量。当不传入容量参数时,容量默认和长度相同。容量参数不能小于长度参数。

package main

import "fmt"

func main() {
a := make([]int, 5)
fmt.Println(a, len(a), cap(a)) //[0 0 0 0 0] 5 5 b := make([]int, 5, 6)
fmt.Println(b, len(b), cap(b)) //[0 0 0 0 0] 5 6 //c := make([]int, 5, 4)
//fmt.Println(c, len(c), cap(c))//报错:len larger than cap in make([]int)
}

2.6. 遍历切片

因为切片是对数组的引用,所以遍历切片也就是在遍历数组。

3. 关于我

Go语言系列(三)之数组和切片的更多相关文章

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

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

  2. GO语言总结(3)——数组和切片

    上篇博文简单介绍了一下Go语言的基本类型——GO语言总结(2)——基本类型,本篇博文开始介绍Go语言的数组和切片. 一.数组 与其他大多数语言类似,Go语言的数组也是一个元素类型相同的定长的序列. ( ...

  3. GO语言数组和切片实例详解

    本文实例讲述了GO语言数组和切片的用法.分享给大家供大家参考.具体分析如下: 一.数组 与其他大多数语言类似,Go语言的数组也是一个元素类型相同的定长的序列. (1)数组的创建. 数组有3种创建方式: ...

  4. go语言之行--数组、切片、map

    一.内置函数 append :追加元素到slice里,返回修改后的slice close :关闭channel delete :从map中删除key对应的value panic  : 用于异常处理,停 ...

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

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

  6. go语言教程之浅谈数组和切片的异同

    Hello ,各位小伙伴大家好,我是小栈君,上次分享我们讲到了Go语言关于项目工程结构的管理,本期的分享我们来讲解一下关于go语言的数组和切片的概念.用法和区别. 在go语言的程序开发过程中,我们避免 ...

  7. go语言的数组和切片区别

    这里不介绍数组和切片的使用技巧,主要看下2者的区别. 首先看下它们的定义: 数组:类型 [n]T 表示拥有 n 个 T 类型的值的数组. 切片:类型 []T 表示一个元素类型为 T 的切片. 看一个数 ...

  8. Go语言--数组、切片、

    3.1 数组--固定大小的连续空间 3.1.1 声明数组 写法 var 数组变量名 [元素数量]T 说明: 变量名就是使用时的变量 元素的数量可以是表达式,最后必须为整型数值 T 可是是任意基本类型, ...

  9. Go语言数组和切片的原理

    目录 数组 创建 访问和赋值 切片 结构 初始化 访问 追加 拷贝 总结 数组和切片是 Go 语言中常见的数据结构,很多刚刚使用 Go 的开发者往往会混淆这两个概念,数组作为最常见的集合在编程语言中是 ...

  10. 使用 PySide2 开发 Maya 插件系列三:qt语言国际化(internationalization)

    使用 PySide2 开发 Maya 插件系列三:qt语言国际化(internationalization) 前言: 这是 qt for python 的语言国际化,基于 UI 的,python 也有 ...

随机推荐

  1. SqlServer 注入技巧

    一.SA权限执行命令,如何更快捷的获取结果? 有显示位 显示位 其实这里的关键并不是有无显示位.exec master..xp_cmdshell 'systeminfo'生成的数据写进一张表的时候,会 ...

  2. 浅谈K-SVD

    由于工作需要,最近刚刚看了一些K-SVD的介绍,这里给自己做一下小节. K-SVD我们一般是用在字典学习.稀疏编码方面,它可以认为是K-means的一种扩展,http://en.wikipedia.o ...

  3. Effective C++ 条款18

    让接口easy被正确使用,不easy被误用 如题目,我们自己的程序接口是面向用户的,程序的目的不可是解决这个问题,并且要让用户easy使用.所以.必须保证我们的程序接口具有非常强的鲁棒性. 怎么保证接 ...

  4. EditText无法失去焦点、失去焦点隐藏软键盘

    很奇怪,我在给EditText设置setOnFocusChangeListener()监听,但是始终未能成功,焦点一直存在,不知其原因,,代码如下: et_username.setOnFocusCha ...

  5. Vue.js与Jquery的比较 谁与争锋 js风暴

    普遍认为jQuery是适合web初学者的起步工具.许多人甚至在学习jQuery之前,他们已经学习了一些轻量JavaScript知识.为什么?部分是因为jQuery的流行,但主要是源于经验开发人员的一个 ...

  6. 加速Android Studio编译速度

    一.修改运行内存 进入项目,菜单栏-help-Edit Custom VM Option   Paste_Image.png 添加或修改为: -Xms2048m -Xmx2048m -XX:MaxPe ...

  7. 数据库批量操作中SqlParameter参数传递的问题

    数据库批量操作 比如会写:update T_AdminUsers set IsEnabled=@IsEnabled where Id in (@ids) 然后再SqlParameter("@ ...

  8. forget word out2

      1★ dictionary / dik ʃ ən əri   dict   2★ fy => faction f æk ʃ ən 派别  

  9. DevExpress v17.2最新版帮助文档下载大全

    DevExpress v17.2.4帮助文档下载列表大全来啦!包含.NET.VCL.HTML/JS系列所有帮助文档,提供CHM和PDF两个版本.除已停止更新的Silverlight.Windows 8 ...

  10. 我也说说Emacs吧(3) - 文件基本操作

    Spacemacs文件基本操作 有了前两讲的积累,我们知道了: 1. 我们既要学习emacs的操作,也要学习vi的操作 2. emacs是一个可扩展的平台,我们并不是从头配置,而是使用一套成熟的集成方 ...