Golang学习系列第三天:学习数组、切片、Map、结构体、指针、函数、接口类型、channel通道
继Golang学习系列第二天:变量、常量、数据类型和流程语句之后,今天开始学习数据类型之高级类型: 派生类型。
学过java的人都知道,java其实就8种基本类型:byte、short、int、long、float、double、char、boolean,但它有引用数据类型:字符串、数组、集合、类、接口等。
而golang也有这样的划分,基本类型(Golang学习系列第二天已学过)和派生类型(不叫引用类型),派生类型有以下几种:数组类型、切片类型、Map类型、结构体类型(struct)、指针类型(Pointer)、函数类型、接口类型(interface)、Channel 类型。
1. 数组类型
数组是具有相同数据类型的元素序列。 数组在声明中定义了固定的长度,因此不能扩展超过该长度。 数组声明为
让我们以代码举例如下
将变量city声明为5个字符串的数组,执行输出
不过也可以在声明数组时设置数组条目,看简洁版
输出结果
甚至,当您传递值时,可以使用省略号来使用隐式长度
输出结果
以不同方式打印数组
注意我们使用带Printf的fmt包以及如何使用%q“动词”来打印每个引用的元素。
如果我们使用Println或%s动词,我们将得到不同的结果
执行后输出如图
多维数组
您还可以创建多维数组,示例代码:
输出结果
2. 切片Slices类型
切片包装数组可为数据序列提供更通用,更强大和更方便的接口。 除了具有明确维数的项(例如转换矩阵)外,Go中的大多数数组编程都是使用切片而不是简单数组完成的。
切片包含对基础数组的引用,如果您将一个切片分配给另一个,则两个切片都引用同一数组。 如果函数采用slice参数,则对slice的元素所做的更改将对调用者可见,这类似于将指针传递给基础数组。
切片指向值数组,并且还包含长度。 切片可以调整大小,因为它们只是另一个数据结构之上的包装。
例如, []T is a slice with elements of type T 表示[] T是具有T类型元素的切片。
举个小例子
2.1 切分切片
可以对切片进行切片,以创建一个指向相同数组的新切片值。表达式为
表示计算从start到end-1(含)的元素的一部分。
注意:start和end是表示索引的整数。
下面举个小demo
输出结果
2.2 制造切片
除了通过立即传递值(切片文字)来创建切片之外,还可以使用make关键字创建切片。 您创建一个特定长度的空切片,然后填充每个条目。
输出结果:
它通过分配清零的数组并返回引用该数组的切片来工作。
2.3 附加到切片
可以通过append方式附加值到切片中
看输出结果
也可以同时附加多个值到切片中,示例代码同时包括两个城市郑洲和濮阳
输出结果
甚至您还可以使用省略号将切片附加到另一个切片
输出结果
注意: 省略号是该语言的内置功能,这意味着该元素是一个集合。 我们无法将字符串类型([] string)类型的元素附加到字符串切片中,只能附加字符串。 但是,在切片后使用省略号(...),表示要附加切片的每个元素。 因为我们要从另一个片段追加字符串,所以编译器将接受操作,因为类型是匹配的。
您显然无法将[] int类型的切片附加到[] string类型的另一个切片中。
2.4 复制切片
切片也可以复制。 在这里,我们创建一个与other_cities长度相同的空切片copycities,并从other_cities复制到copycities中。
输出结果
2.5 切片长度
您可以随时使用len检查切片的长度。
输出结果
2.6 零片
切片的零值为nil。 无切片的长度和容量为0。
输出结果
3. map类型
Map 是一种无序的键值对的集合,Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
输出结果
当不使用上述map时,使用前必须使用make(不是新的)创建map。 nil映射为空,无法分配给它。
输出结果
3.1 操作map
3.1.1 在map中插入或更新元素
3.1.2 查询一个元素
3.1.3 删除一个元素
3.1.4 测试键是否存在并且有值
如果键在m中,则确定为true。 如果不是,则ok为false,elem为map元素类型的零值。 同样,从map中读取时(如果没有按键)则结果是map元素类型的零值。
综上合起来代码如下
输出结果
3.2 map循环
如何在map上进行迭代?您可以使用循环范围来迭代map, 因为映射是无序集合,所以此循环的值可能会有所不同。
输出结果
4. 指针类型(Pointer)
学过C(上学时第一门编程语言就是C)的人都知道, 指针是存放值内存地址的地方, 指针由*定义,根据数据类型定义指针,go也是这么玩的,声明格式如下
var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。
&运算符可用于获取变量的地址。比如
而指针指向的值可以使用*运算符进行访问,示例如下
执行以上代码,输出结果
4.1 空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil,nil 指针也称为空指针。
nil在概念上和其它编程语言的null、None、nil、NULL一样,都指代零值或空值。
一个指针变量通常缩写为 ptr。空指针判断方式:
综上示例代码如下
输出结果
4.2 指针数组
指针数组:简单点说它是一个数组,数组里面的每个元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。格式如下
ptr 为type指针数组,因此每个元素都指向了一个值,只是它的值是指针。
输出结果
4.3 指针的指针
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。
当定义一个指向指针的指针变量时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址。
指向指针的指针变量声明格式如下:
表示指向指针的指针变量为type。访问指向指针的指针变量值需要使用两个 * 号。
输出结果
4.4 通过new函数创建指针
go支持通过new函数创建指针, new函数将类型作为参数,并返回指向新分配的作为参数传递的类型的零值的指针。
输出结果
4.5 指针参数
可以将指针传递给函数
输出结果
特别注意这这两种传参的区别
5. 函数类型
函数是基本的代码块,用于执行一个任务。
Go 语言最少有个 main() 函数。
你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。
函数声明告诉了编译器函数的名称,参数和返回类型。格式如下
函数定义解析:
- func:函数由关键字 func 开始声明
- function_name:函数名称,函数名和参数列表一起构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
5.1 无参无返回值函数
5.2 有参无返回值函数
5.3 无参有返回值函数
5.4 有参有返回值函数
5.5 多个参数多返回值函数
5.6 在函数中预定义返回值的函数
message被定义为返回变量。 因此,定义的变量message将自动返回,而无需在最后的return语句中定义。
5.7 舍弃返回值的函数
5.8 指针传递函数
会改变原来的值,这很容易理解
5.9 可变参数函数
您可以使用Golang中的…运算符来传递数组,也可以使用相同的…运算符来接收参数。
实际输出结果
5.10 匿名函数
没有名字的函数被称为匿名函数
6. 结构体Struct
golang的世界里没有像java一样有class类的概念,但它有像C语言一样的结构体Struct。
结构体是不同字段的类型化集合,结构体用于将数据分组在一起。
例如,如果我们要对User类型的数据进行分组,则定义一个user的属性,其中可以包括姓名,年龄,性别,所在城市。 可以使用以下语法定义结构
输出结果
6.1 方法
方法是带有接收器的一种特殊函数。 接收者可以是值或指针。
输出结果
如图可以看到,现在可以使用点运算符user.describe调用该方法。 注意,接收者是一个指针。 使用指针,我们传递了对该值的引用,因此,如果对方法进行任何更改,它将反映在接收器user中。它也不会创建该对象的新副本,从而节省了内存。
请注意,在上面的示例中,age的值已更改,而name的值未更改,因为方法setName是接收者类型,而setAge是指针类型。
6.2 结构体域字段导出
如果字段名以大写字母开头,则定义该结构的包外部的代码将可对其进行读写。 如果该字段以小写字母开头,则只有该结构包中的代码才能读取和写入该字段。
在同一包中,我们可以访问这些字段,如本示例所示, 由于main也在主程序包中,因此它可以引用u.password并检索存储在其中的值。 通常在结构体中具有未导出的字段,并通过导出的方法来访问它们。类比java里的访问权限private域,public访问方法。
以上程序输出结果
6.3 创建匿名结构体
匿名仅创建新的结构变量,而不定义任何新的结构体类型。
以上程序代码中,定义了一个匿名结构变量emp, 正如我们已经提到的,该结构称为匿名结构,因为它仅创建一个新的结构变量emp,而不定义任何新的结构类型。
输出结果
6.4 嵌套结构体
结构可能包含一个字段,而该字段又是一个结构。 这些类型的结构称为嵌套结构。
比如我们经常网上购物时,填写订单接收地址(家里,公司,寄存点)
上面程序中的Person结构具有一个字段地址address,该字段地址又是一个结构体。
6.5 指向结构体的指针
您可以使用&运算符获取指向结构体的指针
以上示例所示,Go使您可以直接通过指针访问结构体的字段。
输出结果
6.6 结构体是值类型
结构体是值类型,当您将一个结构体变量分配给另一个时,将创建并分配该结构的新副本。 同样,当您将结构体传递给另一个函数时,该函数将获得其自己的结构副本。
输出结果
7. 接口Interface类型
Go编程提供了另一种称为接口的数据类型,它表示一组方法签名。
struct数据类型实现接口中定义的方法。
输出结果
8. Channels类型
通道是一种类型化的管道,可以使用通道运算符<-发送和接收值。
注意:数据按箭头方向流动
和其他数据类型类似,通道使用前必须先创建,其初始值是 nil。创建通道的语法格式如下:
- [value type] 定义的是 Channel 中所传输数据的类型。
- [channel type] 定义的是 Channel 的类型,其类型有以下三种:
- “chan” 可读可写——“chan int” 则表示可读写 int 数据的 channel
- "chan<-" 仅可写——“chan<- float64” 则表示仅可写64位 float 数据的 channel
- “<-chan” 仅可读——“<-chan int” 则表示仅可读 int 数据的 channel
- [capacity] 是一个可选参数,其定义的是 channel 中的缓存区 (buffer)。如果不填则默认该 channel 没有缓冲区 (unbuffered)。对于没有缓冲区的 channel,消息的发送和收取必须能同时完成,否则会造成阻塞并提示死锁错误。
比如我们想创建了一个读写 int 类型,buffer 长度 100 的 channel c1,则如下:
通过此通道,我们可以发送int类型的数据。 我们可以在此通道中发送和接收数据
输出结果:
接收方通道等待,直到发送方将数据发送到通道。
8.1 单向通道
在有些情况下,我们希望通过通道接收数据但不发送数据, 为此我们还可以创建一个单向通道。 让我们看一个简单的例子:
在上面的示例代码中,sc是go协程,该例程只能将消息发送到通道,但不能接收消息。
执行代码输出结果
8.2 缓存通道(Buffered channel)
在Golang中可以创建一个缓冲通道。 对于缓冲的通道,如果缓冲区已满,则将阻止发送到该通道的消息。 让我们看一个小例子
实际上超过缓冲最大值,要报错
那怎么办呢,还好有以下解决方法
输出结果
测验结果
总结: golang就是综合性编程语言,幸好以前做过java、pyhon、JavaScript开发,上学时又学过C语言(第一编程语言),故学起golang很快,极少部分是golang本身特有的。
一句话概括:golang是几种语言的混合体,外加自己的特性。如果不考虑语言本身,你会觉得像是在写C,有时又像是写python,写函数时又像JavaScript中函数的变体。
参考:
-
Golang Tutorial — from zero to hero https://milapneupane.com.np/2019/07/06/learning-golang-from-zero-to-hero/
-
Understanding Maps in Go https://www.digitalocean.com/community/tutorials/understanding-maps-in-go
-
Golang Maps https://www.geeksforgeeks.org/golang-maps/
-
Golang Tutorial – Learn Golang by Examples https://www.edureka.co/blog/golang-tutorial/#map
-
The anatomy of Slices in Go https://medium.com/rungo/the-anatomy-of-slices-in-go-6450e3bb2b94
-
GoLang Tutorial - Structs and receiver methods - 2020 https://www.bogotobogo.com/GoLang/GoLang_Structs.php
-
Golang Cheatsheet: Functions https://ado.xyz/blog/golang-cheatsheet-functions/
-
Ultimate Guide to Go Variadic Functions https://medium.com/m/global-identity?redirectUrl=https%3A%2F%2Fblog.learngoprogramming.com%2Fgolang-variadic-funcs-how-to-patterns-369408f19085
-
Golang Methods Tutorial with Examples https://www.callicoder.com/golang-methods-tutorial/
-
Go Best Practices: Should you use a method or a function? https://flaviocopes.com/golang-methods-or-functions/
-
Methods that satisfy interfaces in golang https://suraj.io/post/golang-methods-interfaces/
-
Pass by pointer vs pass by value in Go https://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value/
-
Go Data Structures: Interfaces https://research.swtch.com/interfaces
-
How to Define and Implement a Go Interface https://code.tutsplus.com/tutorials/how-to-define-and-implement-a-go-interface--cms-28962
-
Methods and Interfaces in Go https://dev-pages.info/golang-interfaces/
-
Go (Golang) - understanding the object oriented features with structs, methods, and interfaces https://unixsheikh.com/articles/go-understanding-the-object-oriented-features-with-structs-methods-and-interfaces.html#interfaces-in-go
-
理解 Golang 的 Channel 类型 https://studygolang.com/articles/25805
-
Anatomy of Channels in Go - Concurrency in Go https://medium.com/rungo/anatomy-of-channels-in-go-concurrency-in-go-1ec336086adb