《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语言之字符串、指针、数组、切片、结构struct、面向对象

    一: 字符串 概述: Go 语言将字符串作为 种原生的基本数据类型,字 符串的初始化可以使用字符串字面量. (1)字符串是常量,可以通过类 数组 索引访问其字节单元,但是不能修改某个字节的值 (2)宇 ...

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

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

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

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

随机推荐

  1. Numerical Sequence (Hard vision) 题解

    The only difference between the easy and the hard versions is the maximum value of \(k\). You are gi ...

  2. mysql根据逗号分割的字符串去关联查询另外一个表的数据

    1.说明 在做显示数据的时候,一个字段会存那种逗号分割的字符串,那如何去根据逗号分割字符串去查询另一个表的数据呢? 首先我们查看一下需要显示的数据 select * from company wher ...

  3. Layui文本框限制正整数

    <input type="text" name="Number" lay-verify="required|integer" plac ...

  4. .Net Core微服务入门全纪录(八)——Docker Compose与容器网络

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 前言 上一篇[.Net Core微服务入门全纪录(七)--IdentityServer4-授权认证]中使用IdentityServer4 ...

  5. ES2020的这些新功能令人期待

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://blog.bitsrc.io/es2020-has-been-finalized- ...

  6. scala 数据结构(一):数据结构简介

    1 数据结构特点 scala集合基本介绍 1)Scala同时支持不可变集合和可变集合,不可变集合可以安全的并发访问 2)两个主要的包: 不可变集合:scala.collection.immutable ...

  7. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  8. PowerShell创建参考窗口

    背景 平常我们经常遇到这样一个问题,在使用一个窗口工作时常常需要参考其他窗口的文字或图片,此时就需要频繁切换窗口:或者是看视频时需要参考前面进度的画面:或者是阅读或写文档时需要参考其他位置的文字,这时 ...

  9. IE9+的树状下拉菜单,支持多选

    //JS核心代码function treeBox(Config){var el=eval(Config.el);var w=Config.width;var h=Config.height;var d ...

  10. Ethical Hacking - Web Penetration Testing(2)

    INFORMATION GATHERING IP address. Domain name Info. Technologies used. Other websites on the same se ...