在上面的章节里面,我们讲过Go内置的基本数据类型。现在我们来看一下Go内置的高级数据类型,数组,切片和字典。

数组(Array)

数组是一个具有相同数据类型的元素组成的固定长度有序集合。比如下面的例子

var x []int

表示数组x是一个整型数组,而且数值的长度为5。

Go提供了几种不同的数组定义方法。

最基本的方式就是使用var关键字来定义,然后依次给元素赋值对于没有赋值的元素,默认为零值。比如对于整数,零值就是0,浮点数,零值就是0.0,字符串,零值就是””,对象零值就是nil。

package main
import (
"fmt"
)
func main() {
var x []int
x[] =
x[] =
x[] =
x[] =
x[] =
var sum int
for _, elem := range x {
sum += elem
}
fmt.Println(sum)
}

在上面的例子中,我们首先使用var关键字来声明,然后给出数组名称x,最后说明数组为整型数组,长度为5。然后我们使用索引方式给数组元素赋值。在上面的例子中,我们还使用了一种遍历数组元素的方法。该方法利用Go语言提供的内置函数range来遍历数组元素。range函数可以用在数组,切片和字典上面。当range来遍历数组的时候返回数组的索引和元素值。在这里我们是对数组元素求和,所以我们对索引不感兴趣。在Go语言里面,当你对一个函数返回值不感兴趣的话,可以使用下划线(_)来替代它。另外这里如果我们真的定义了一个索引,在循环结构里面却没有使用索引,Go语言编译的时候还是会报错的。所以用下划线来替代索引变量也是唯一之举了。最后我们输出数组元素的和。

还有一种方式,如果知道了数组的初始值。可以像下面这样定义。

package main
import (
"fmt"
)
func main() {
var x = []int{, , , }
x[] =
var sum int
for _, i := range x {
sum += i
}
fmt.Println(sum)
}

当然,即使你不知道数组元素的初始值,也可以使用这样的定义方式。

package main
import (
"fmt"
)
func main() {
var x = []int{}
x[] =
x[] =
x[] =
x[] =
x[] =
var sum int
for _, i := range x {
sum += i
}
fmt.Println(sum)
}

在这里我们需要特别重视数组的一个特点,就是数组是有固定长度的。

但是如果我们有的时候也可以不显式指定数组的长度,而是使用...来替代数组长度,Go语言会自动计算出数组的长度。不过这种方式定义的数组一定是有初始化的值的。

package main
import (
"fmt"
)
func main() {
var x = [...]string{
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"}
for _, day := range x {
fmt.Println(day)
}
}

在上面的例子中,还需要注意一点就是如果将数组元素定义在不同行上面,那么最后一个元素后面必须跟上}或者,。上面的例子也可以是这样的。

package main
import (
"fmt"
)
func main() {
var x = [...]string{
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
}
for _, day := range x {
fmt.Println(day)
}
}

Go提供的这种可以自动计算数组长度的方法在调试程序的时候特别方便,假设我们注释掉上面数组x的最后一个元素,我们甚至不需要去修改数组的长度。

 

切片(Slice)

在上面我们说过数组是有固定长度的有序集合。这也就是说一旦数组长度定义,你将无法在数组里面多添加哪怕一个元素。数组的这种特点有的时候会成为很大的缺点,尤其是当数组的元素个数不确定的情况下。

所以切片诞生了。

切片和数组很类似,甚至你可以理解成数组的子集。但是切片有一个数组所没有的特点,那就是切片的长度是可变的

严格地讲,切片有容量(capacity)长度(length)两个属性。

首先我们来看一下切片的定义。切片有两种定义方式,一种是先声明一个变量是切片,然后使用内置函数make去初始化这个切片。另外一种是通过取数组切片来赋值。

package main
import (
"fmt"
)
func main() {
var x = make([]float64, )//容量5,长度5
fmt.Println("Capcity:", cap(x), "Length:", len(x))
var y = make([]float64, , )//容量10,长度5
fmt.Println("Capcity:", cap(y), "Length:", len(y))
for i := ; i < len(x); i++ {
x[i] = float64(i)
}
fmt.Println(x)
for i := ; i < len(y); i++ {
y[i] = float64(i)
}
fmt.Println(y)
}

结果

上面我们首先用make函数定义切片x,这个时候x的容量是5,长度也是5。然后使用make函数定义了切片y,这个时候y的容量是10,长度是5。然后我们再分别为切片x和y的元素赋值,最后输出。

所以使用make函数定义切片的时候,有两种方式,一种只指定长度,这个时候切片的长度和容量是相同的。另外一种是同时指定切片长度和容量。虽然切片的容量可以大于长度,但是赋值的时候要注意最大的索引仍然是len(x)-1。否则会报索引超出边界错误。

另外一种是通过数组切片赋值,采用[low_index:high_index]的方式获取数值切片,其中切片元素包括low_index的元素,但是不包括high_index的元素

package main
import (
"fmt"
)
func main() {
var arr1 = []int{, , , , }
var s1 = arr1[:]//第二个元素后面,第四个元素前面的所有元素
var s2 = arr1[:]//第三个元素前面的所有元素
var s3 = arr1[:]//第二个元素后面的所有元素
var s4 = arr1[:]//等效于var s4 = arr1
fmt.Printf("arr1的类型为%T\n",arr1)
fmt.Printf("s1的类型为%T\n",s1)
fmt.Println(s1)
fmt.Println(s2)
fmt.Println(s3)
fmt.Println(s4)
}

结果为

在上面的例子中,我们还省略了low_index或high_index。如果省略了low_index,那么等价于从索引0开始;如果省略了high_index,则默认high_index等于len(arr1),即切片长度。

这里为了体现切片的长度可以变化,我们看一下下面的例子:.

package main
import (
"fmt"
)
func main() {
var arr1 = make([]int, , )
for i := ; i < len(arr1); i++ {
arr1[i] = i
}
fmt.Println(arr1)
arr1 = append(arr1, , , , )
fmt.Println("容量:", cap(arr1), "长度:", len(arr1))
fmt.Println(arr1)
}

输出结果为

这里我们初始化arr1为容量10,长度为5的切片,然后为前面的5个元素赋值。然后输出结果。然后我们再使用Go内置方法append来为arr1追加四个元素,这个时候再看一下arr1的容量和长度以及切片元素,我们发现切片的长度确实变了。

另外我们再用append方法给arr1多追加几个元素,试图超过arr1原来定义的容量大小。

package main
import (
"fmt"
)
func main() {
var arr1 = make([]int, , )
for i := ; i < len(arr1); i++ {
arr1[i] = i
}
arr1 = append(arr1, , , , , , )
fmt.Println("容量:", cap(arr1), "长度", len(arr1))
fmt.Println(arr1)
}

输出结果为:

我们发现arr1的长度变为11,因为元素个数现在为11个。另外我们发现arr1的容量也变了,变为原来的两倍。

这是因为Go在默认的情况下,如果追加的元素超过了容量大小,Go会自动地重新为切片分配容量,容量大小为原来的两倍

上面我们介绍了,可以使用append函数给切片增加元素,现在我们再来介绍一个copy函数用来从一个切片拷贝元素到另一个切片

package main
import (
"fmt"
)
func main() {
slice1 := []int{, , , , , }
slice2 := make([]int, , )
copy(slice2, slice1)
fmt.Println(slice1)
fmt.Println(slice2)
}

输出结果

在上面的例子中,我们将slice1的元素拷贝到slice2,因为slice2的长度为5,所以最多拷贝5个元素。

总结一下,数组和切片的区别就在于[]里面是否有数字或者...。因为数值长度是固定的,而切片是可变的。

字典(Map)

字典是一组无序的键值对集合

字典也叫做关联数组,因为数组通过索引来查找元素,而字典通过来查找元素。当然,很显然的,字典的键是不能重复的。如果试图赋值给同一个键,后赋值的值将覆盖前面赋值的值。

字典的定义也有两种,一种是初始化数据的定义方式,另一种是使用神奇的make函数来定义。

package main
import (
"fmt"
)
func main() {
var x = map[string]string{
"A": "Apple",
"B": "Banana",
"O": "Orange",
"P": "Pear",
}
for key, val := range x {
fmt.Println("Key:", key, "Value:", val)
}
}

输出结果为

在上面的例子中,我们定义了一个string:string的字典,其中[]之间的是键类型,右边的是值类型。另外我们还看到了range函数,此函数一样神奇,可以用来迭代字典元素,返回key:value键值对。当然如果你对键或者值不感兴趣,一样可以使用下划线(_)来忽略返回值。

可以这样

for val := range x {
fmt.Println( "Value:", val)
}

等效于

for _,val := range x {
fmt.Println( "Value:", val)
}

也可以这样

    for key, _ := range x {
fmt.Println("Key:", key)
}

下一个例子

package main
import (
"fmt"
)
func main() {
var x map[string]string //定义x为map变量
x = make(map[string]string)//初始化x
//给X的元素赋值
x["A"] = "Apple"
x["B"] = "Banana"
x["O"] = "Orange"
x["P"] = "Pear"
for key, val := range x {
fmt.Println("Key:", key, "Value:", val)
}
}

上面的方式就是使用了make函数来初始化字典,试图为未经过初始化的字典添加元素会导致运行错误,你可以把使用make函数初始化的那一行注释掉,然后看一下。

当然上面的例子中,我们可以把定义和初始化合成一句。

x := make(map[string]string)//定义x并初始化

现在我们再来看一下字典的数据访问方式。如果你访问的元素所对应的键存在于字典中,那么没有问题,如果不存在呢?

这个时候会返回零值。对于字符串零值就是””,对于整数零值就是0。但是对于下面的例子:

package main
import (
"fmt"
)
func main() {
x := make(map[string]int)
x["A"] =
x["B"] =
fmt.Println(x["C"])
}

输出结果为

在这个例子中,很显然不存在键C,但是程序的输出结果为0,这样就和键A对应的值混淆了。

Go提供了一种方法来解决这个问题:

package main
import (
"fmt"
)
func main() {
x := make(map[string]int)
x["A"] =
x["B"] =
x["O"] =
x["P"] =
if val, ok := x["C"]; ok {
fmt.Println(val)
}
}

上面的例子中,我们可以看到事实上使用x["C"]的返回值有两个,一个是值,另一个是是否存在此键的bool型变量,所以我们看到ok为true的时候就输出键C的值,如果ok为false,那就是字典中不存在这个键。

现在我们再来看看Go提供的内置函数delete,这个函数可以用来从字典中删除元素

package main
import (
"fmt"
)
func main() {
x := make(map[string]int)
x["A"] =
x["B"] =
x["C"] =
x["D"] =
fmt.Println("Before Delete")
fmt.Println("Length:", len(x))
fmt.Println(x)
delete(x, "A")
fmt.Println("After Delete")
fmt.Println("Length:", len(x))
fmt.Println(x)
}

输出结果为

我们在删除元素前查看一下字典长度和元素,删除之后再看一下。这里面我们还可以看到len函数也可以用来获取字典的元素个数。当然如果你试图删除一个不存在的键,那么程序也不会报错,只是不会对字典造成任何影响。

最后我们再用一个稍微复杂的例子来结束字典的介绍。

我们有一个学生登记表,登记表里面有一组学号,每个学号对应一个学生,每个学生有名字和年龄。

package main
import (
"fmt"
)
func main() {
var facebook = make(map[string]map[string]int)
facebook[""] = map[string]int{"Jemy": }
facebook[""] = map[string]int{"Andy": }
facebook[""] = map[string]int{"Bill": }
for stu_no, stu_info := range facebook { //stu_no为key ,stu_info为key对应的值
fmt.Println("Student:", stu_no)
for name, age := range stu_info {
fmt.Println("Name:", name, "Age:", age)
}
fmt.Println()
}
}

输出结果为

当然我们也可以用初始化的方式定义字典:

package main
import (
"fmt"
)
func main() {
var facebook = map[string]map[string]int{
"": {"Jemy": },
"": {"Andy": },
"": {"Bill": },
}
for stu_no, stu_info := range facebook {
fmt.Println("Student:", stu_no)
for name, age := range stu_info {
fmt.Println("Name:", name, "Age:", age)
}
fmt.Println()
}
}

输出结果是一样的。

 



												

换个语言学一下 Golang (6)——数组,切片和字典的更多相关文章

  1. [golang note] 数组切片

    数组 √ golang数组包含的每个数据称为数组元素(element),数组包含的元素个数被称为数组长度(length). √ golang数组的长度在定义后不可更改,并且在声明时可以是一个常量或常量 ...

  2. 换个语言学一下 Golang (3)——数据类型

    在 Go 编程语言中,数据类型用于声明函数和变量. 数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存. Go 语言按类别有以下几种 ...

  3. 换个语言学一下 Golang (1)

    做技术的总是有些拗.这么多年一直在.net的框框里打转转.直到现在市场上.net工作越来越难找,项目越来越老才发现不做出改变不行了.就从学习Go开始吧. Go语言的特点 简洁.快速.安全 并行.有趣. ...

  4. 换个语言学一下 Golang (13)——Web表单处理

    介绍 表单是我们平常编写Web应用常用的工具,通过表单我们可以方便的让客户端和服务器进 行数据的交互.对于以前开发过Web的用户来说表单都非常熟悉.表单是一个包含表单元素的区域.表单元素是允许用户在表 ...

  5. 换个语言学一下 Golang (12)——Web基础

    一.web工作方式 我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容.在这个看似简单的用户行为背后,到底隐藏了些什么呢?对于普通的上网过程,系统其实是这样做的 ...

  6. 换个语言学一下 Golang (9)——结构体和接口

    基本上到这里的时候,就是上了一个台阶了.Go的精华特点即将展开. 结构体定义 上面我们说过Go的指针和C的不同,结构体也是一样的.Go是一门删繁就简的语言,一切令人困惑的特性都必须去掉. 简单来讲,G ...

  7. 换个语言学一下 Golang (7)——使用函数

    什么是函数 函数,简单来讲就是一段将输入数据转换为输出数据的公用代码块.当然有的时候函数的返回值为空,那么就是说输出数据为空.而真正的处理过程在函数内部已经完成了. 想一想我们为什么需要函数,最直接的 ...

  8. 换个语言学一下 Golang (11)——使用包和测试

    Go天生就是为了支持良好的项目管理体验而设计的. 包 在软件工程的实践中,我们会遇到很多功能重复的代码,比如去除字符串首尾的空格.高质量软件产品的特点就是它的部分代码是可以重用的,比如你不必每次写个函 ...

  9. 换个语言学一下 Golang (5)——运算符

    运算符用于在程序运行时执行数学或逻辑运算. Go 语言内置的运算符有: 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 其他运算符 接下来让我们来详细看看各个运算符的介绍. 算术运算符 下表 ...

随机推荐

  1. 从太空到地球某个位置的轨迹录像制作 | Earth Zoom in/out Tutorial (Record Video)

    视频教程:Google Earth - Earth Zoom in/out Tutorial (Record Video) 下载google earth 在search里输入你想要去的地名 zoom ...

  2. DB2数据库基础

    一.DB2数据库安装教程 DB2安装教程:https://jingyan.baidu.com/article/e75057f2f59ef9ebc91a8905.html 二.DB2常用命令 1. 打开 ...

  3. Django入门2开发工具pycharm的配置

    在pycharm中新建django项目 查看django是否安装成功 运行django 设置pycharm快捷键 设置python模板,新建的python文件就会自动生成一些信息 设置django启动 ...

  4. EasyNVR网页摄像机无插件H5、谷歌Chrome直播方案安装使用常见问题的分析

    EasyNVR对于互联网的视频直播还是有着一定的贡献的.为了方便用户的体验使用,我们也在互联网上放置了对应的试用版本,并且也会随着功能是更新也会定期的更新上去.软件包也会配置对应的使用文档和说明. 许 ...

  5. 安装 python 爬虫框架 Scrapy

    官方安装说明文档:https://doc.scrapy.org/en/latest/intro/install.html#installing-scrapy 一.scrapy 需要以下依赖 二.一般来 ...

  6. Vue NGINX Apache 404 问题解决

    location ^~/html/dist { #alias /home/server/webapps/vuejs-admin/; index index.html; try_files $uri $ ...

  7. 使用Win10自带的截图工具

    Win10自带的截图工具还是可以的,快捷键win+shift+s 截完图之后会有一个通知,你可以对截图进行二次编辑,编辑之后默认是立即保存的 再搭配着Fu图床工具使用,简直太爽了 讲一下怎么打开通知

  8. 最常见的Java面试题及答案汇总(四)

    反射 57. 什么是反射? 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力 Java反射: 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能 ...

  9. python:字符串中提取特定的数据

    在日志文件中有一大堆,格式相同的文本,需要提取出接口耗时的时间 >>> 运单号:71742507538566,快递100接口耗时:8,返回结果:[{"lengthPre&q ...

  10. odoo self.ensure_one()

    源码: def ensure_one(self): """ Verifies that the current recorset holds a single recor ...