《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. T-SQL 常用DDL语句

    数据库操作 删除数据库 drop database database_name(数据库名) 修改数据库名 alter database database_name(原数据库名) modify name ...

  2. AngularJs $q 承诺与延迟

    $q 一个帮助处理异步执行函数的服务.当他们做完处理时,使用它们的返回值(或异常). 受 Kris Kowa’s Q 的启发,这是一个实现promise/deferred对象的启用. $q的两种方式- ...

  3. WP7 MD5加密

    WP7不支持MD5加密,在网上找了一个实现MD5加密的算法. //Copyright (c) Microsoft Corporation. All rights reserved. using Sys ...

  4. github的使用与问题

    GIT密钥的生成步骤 一 .设置Git的user name和email: $ git config --global user.name "name" $ git config - ...

  5. 微信小程序推广技巧、营销方案

    小程序已经成功上线了!那么,小程序线下如何推广?线下门店如何玩转小程序呢? 1.附近的小程序,让商家曝光率更高 小 程序自带“附近的小程序”功能,利用LBS定位功能提高商家专属微信小程序的曝光度,用户 ...

  6. tmux resurrect 配置

    概述 tmux 用了很长时间了, 快捷键定制了不少, 唯一的遗憾是没法保存 session, 每次关机重开之后, 恢复不到之前的 tmux session. 虽然也能忍受, 但是每天都手动打开之前的 ...

  7. docker 基础

    概述 起源 2013 年由 DotCloud 公司开源出来的容器管理工具 DotCloud 公司是一家 PAAS 服务提供商,从 docker 的出身也可以看出它的主要功能和方向 技术原理 开始时是基 ...

  8. k8s docker集群搭建

    一.Kubernetes系列之介绍篇   •Kubernetes介绍 1.背景介绍 云计算飞速发展 - IaaS - PaaS - SaaS Docker技术突飞猛进 - 一次构建,到处运行 - 容器 ...

  9. Java反射机制二 获取方法的返回值或参数的泛型信息

    在使用反射机制时,我们经常需要知道方法的参数和返回值类型,很简单  ,下面上示例,示例中的两个方法非常相似 package deadLockThread; import java.lang.refle ...

  10. 实验吧—密码学——WP之 传统知识+古典密码

    仔细读题,发现有价值的信息: 几个不同的年份:“+甲子”:key值结构 首先我们并不知道这些年份在这里代表着什么,那么我们就去百度一下发现了如下所示的六十甲子顺序表 而在表中每个年份前都有数字,将他们 ...