Skip to main content

复合数据类型

在go语言中,复合数据类型包括:数组,slice,map,结构体

数组

数组是由固定长度的特定类型元素组成的序列.

  • 类型和长度

数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定.

数组元素通过索引进行访问与赋值.

go
1

根据数组字面量中,如果在数组的长度位置出现的是...省略号,则表示数组的长度是根据初始化值得个数来计算.

go
1

数组类型由数据类型及长度组成.

数组是可以比较的?

如果一个数组元素类型是可以比较的,那么数据类型也是可以相互比较的,可直接使用==!=比较运算符进行比较

只有当两个数组类型相同,且所有索引位上的元素都相等时数组才是相等的.

数组比较有什么用呢?

slice

slice代表可变长的序列,

slice和数组很像,只是没有固定长度而已.

slice的三要素,指针,类型及容量.

容量与长度

所谓容量是指一个slice所能容纳元素的最大

可以通过内置函数len来获取slice所拥有的元素数量,

splice和数组的关系

向slice追加元素--append

go
1
2
3
4
5
6
7
8
9
10
11
func main() {
nums := []int{}
nums = append(nums, 1)
nums = append(nums, 2, 3, 4, 5)
for i, num := range nums {
fmt.Println(i, num)
}
}

append是go语言的一个内置函数,可以一次追加多个元素

在append不确定底层是否进行了扩容操作,所以通常的做法是将append函数的返回值赋值给输入的slice变量

map

Map又称之为哈希表,是一个无序的key/value对的集合,是一种巧妙且实用的数据结构.

map类型可以写为map [K]V,其中K和V分别对应key和value.

在map中所有元素的key都有着相同的数据类型,value也有着相同的数据类型,但key和value的类型没有必然联系,可不相同.

  • map中的key必须是支持==比较运算符的数据类型

所以map可以通过测试key是否相等来判断是否已经存在.

创建map

  1. 使用make内置函数
  2. 使用map字面量
go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
//使用make内置函数创建map
m1 := make(map[int]string)
m1[1] = "one"
// m = append(m, map[int]string{1: "one"})
fmt.Printf("%+v", m1)
//map字面量创建map
m2 := map[int]string{
1: "one",
2: "two",
}
fmt.Printf("%+v", m2)
}

在初始化map时,vscode不会自动给map添加逗号

对于map类型,元素之间的逗号必不可少,尤其是最后一个元素的逗号,更是不能省,否则会报错.

关于map的常见操作

追加元素

删除元素

判断key是否存在?

在通过索引下表来访问map时将产生一个value,但该value并不一定时索引所对应的值,当索引不存在时,所获取到的值是map类型的value的默认值而已,所以通常的做法如下:

通过map索引下表语法将产生两个值,第一个值是索引所对应的值,第二个值是一个布尔值,用于报告元素是否真的存在?

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func main() {
nums := make(map[int]string)
nums[1] = "one"
nums[2] = "two"
nums[3] = "three"
if num, ok := nums[2]; ok {
fmt.Println("exists index 2", num)
} else {
fmt.Println("not exists index 2")
}
if num, ok := nums[4]; ok {
fmt.Println("exists index 4", num)
} else {
fmt.Println("not exists index 4")
}
}

num,ok:=nums["xx"],是go语言中处理map查询的标准模式.

  • 考虑索引存在性检查
  • 限制变量作用域,避免污染变量

根据key获取value?

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
nums := make(map[int]string)
nums[1] = "one"
nums[2] = "two"
nums[3] = "three"
//创建切片的高性能写法
// keys := []int{}
keys := make([]int, 0, len(nums))
for key, _ := range nums {
keys = append(keys, key)
}
fmt.Printf("%+v", keys)
}

获取所有key,获取所有value?

结构体

称之为结构体元素,通常结构体元素一行一个,如果多个元素类型相同,可以写在同一行.

空结构体

如果结构体没有任何成员的话就是空结构体,写作struct,

结构体初始化

  1. 按照结构体的成员进行初始化

注意事项:

这两种初始化方式不能混合使用.

按照结构体成员的方式更加常用,不会因为结构体成员的修改,而牵一发动全身.

在go语言中,当花括号单独占一行,最后一个元素的逗号必须保留.

go
1

结构体可以作为函数的参数和返回值

当结构体作为函数参数时,并要在函数内部修改结构体成员的话,必须用指针传入.

go语言中,所有的参数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量.

go
1
2
3
4
5
6
7
8
9
func main() {
p1 := &point{10, 20}
p2 := new(point)
*p2 = point{30, 50}
fmt.Printf("%+v\n%+v\n", p1, p2)
}

匿名成员

结构体嵌套

疑惑:关于结构体与map的区别

map是键值对的集合,是一种常用的数据结构,而结构体是复合数据类型,

map要求key和value必须是相同的数据类型.

map是一种动态键值对集合,适合存储在运行时才能确定的字段或需要频繁增删的成员,而结构体是静态类型,用于定义固定结构的数据类型.

JSON

所谓json是指js对象表示法,是一种用于接收和发送结构化信息的标准协议.

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type student struct {
Name string
Age, Grade int
School string
}
func main() {
students := []student{
{"张三", 10, 3, "清华大学"},
{"李四", 20, 4, "北京大学"},
{"王五", 30, 5, "上海交通大学"},
}
data, err := json.Marshal(students)
if err != nil {
fmt.Println("json.Marshal err:", err)
return
}
fmt.Println(string(data))
fmt.Printf("%s\n", data)
}

在初次运行时,打印结果显示空白,因为所定义的结构体成员首字母未大写所导致.

marshal函数的返回结果没有缩进,不便于阅读.为了生成便于阅读的格式,

只有导出的结构体成员才会被编码.

结构体的成员tag

结构体的成员tag可以是任意的字符串面值,但通常是一系列用空格分割的key:value键值对序列,因为值周包含双引号字符,因此tag一般用原生字符串面值的形式书写.

omitempty,表示当go语言结构体成员为空或零值时,不生成json对象.

复合数据类型均可转为json,

数组转json

map转json

结构体转json

通常数据与结构体

文本和HTML模板

如果只是简单的格式化,使用fmt.Printf是完全够的,但有时需要复杂打印格式,

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"editor.fontSize":18,
"editor.suggestSelection": "first",
// vscode默认启用了根据文件类型自动设置tabsize的选项
"go.formatTool": "gofmt",
"editor.detectIndentation": false,
"editor.formatOnSave": true,
// 自动修复
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
...
}