《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. 手把手教你玩转Git

    文章已托管到GitHub,大家可以去GitHub查看下载!并搜索关注微信公众号 码出Offer 领取各种学习资料! 微信公众号码出Offer Git应用 一.初识Git 1.1 Git的简史 同生活中 ...

  2. asp.net mvc企业实战技能汇总

  3. ant design pro 当中改变ant design 组件的样式和 数据管理

    ant design pro 简介 官网简介 链接 https://pro.ant.design/docs/getting-started-cn 项目结构 https://github.com/ant ...

  4. 武汉百得思维Java面试总结

    武汉百得思维Java面试总结 在一个愉快的下午,我们并不十分愉快地参加了宣讲会.那是国庆的前两天,大家正在为放假的愉悦所兴奋也在为找工作而紧张. 在听过一阵胡吹乱侃之后,大家都昏昏欲睡,于是终于迎来了 ...

  5. 02 drf源码剖析之快速了解drf

    02 drf源码剖析之快速了解drf 目录 02 drf源码剖析之快速了解drf 1. 什么是drf 2. 安装 3. 使用 3. DRF的应用场景 1. 什么是drf drf是一个基于django开 ...

  6. bzoj2056gift? 高精度?*

    bzoj2056gift? 高精度? 题意: 给出abcdefghi,求2^a+2^b+2^c+2^d+2^e+2^f+2^g+2^h+i.a~h≤60,i≤2^63 题解: 发现只有极限数据才会爆u ...

  7. Linux下一只五颜六色的「猫」

    大家好,我是良许. 有使用过 Linux 系统的小伙伴,肯定会使用过 cat 这个命令.当然,在 Linux 下,此猫非彼猫,这里的 cat 并不代表猫,而是单词 concatenate 的缩写. c ...

  8. day8 python 列表,元组,集合,字典的操作及方法 和 深浅拷贝

    2.2 list的方法 # 增 list.append() # 追加 list.insert() # 指定索引前增加 list.extend() # 迭代追加(可迭代对象,打散追加) # 删 list ...

  9. nginx一个端口配置多个不同服务映射

    upstream tomcat_server{ server 127.0.0.1:8087; server 192.168.149.117:8088; } server { listen 8088; ...

  10. 从连接器组件看Tomcat的线程模型——BIO模式

    在高版本的Tomcat中,默认的模式都是使用NIO模式,在Tomcat 9中,BIO模式的实现Http11Protocol甚至都已经被删除了.但是了解BIO的工作机制以及其优缺点对学习其他模式有有帮助 ...