复合数据类型
在go语言中,复合数据类型包括:数组,slice,map,结构体
数组
数组是由固定长度的特定类型元素组成的序列.
- 类型和长度
数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定.
数组元素通过索引进行访问与赋值.
根据数组字面量中,如果在数组的长度位置出现的是...省略号,则表示数组的长度是根据初始化值得个数来计算.
数组类型由数据类型及长度组成.
数组是可以比较的?
如果一个数组元素类型是可以比较的,那么数据类型也是可以相互比较的,可直接使用==或!=比较运算符进行比较
只有当两个数组类型相同,且所有索引位上的元素都相等时数组才是相等的.
数组比较有什么用呢?
slice
slice代表可变长的序列,
slice和数组很像,只是没有固定长度而已.
slice的三要素,指针,类型及容量.
容量与长度
所谓容量是指一个slice所能容纳元素的最大
可以通过内置函数len来获取slice所拥有的元素数量,
splice和数组的关系
向slice追加元素--append
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
- 使用make内置函数
- 使用map字面量
func main() {//使用make内置函数创建mapm1 := make(map[int]string)m1[1] = "one"// m = append(m, map[int]string{1: "one"})fmt.Printf("%+v", m1)//map字面量创建mapm2 := map[int]string{1: "one",2: "two",}fmt.Printf("%+v", m2)}
在初始化map时,vscode不会自动给map添加逗号
对于map类型,元素之间的逗号必不可少,尤其是最后一个元素的逗号,更是不能省,否则会报错.
关于map的常见操作
追加元素
删除元素
判断key是否存在?
在通过索引下表来访问map时将产生一个value,但该value并不一定时索引所对应的值,当索引不存在时,所获取到的值是map类型的value的默认值而已,所以通常的做法如下:
通过map索引下表语法将产生两个值,第一个值是索引所对应的值,第二个值是一个布尔值,用于报告元素是否真的存在?
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?
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,
结构体初始化
- 按照结构体的成员进行初始化
注意事项:
这两种初始化方式不能混合使用.
按照结构体成员的方式更加常用,不会因为结构体成员的修改,而牵一发动全身.
在go语言中,当花括号单独占一行,最后一个元素的逗号必须保留.
结构体可以作为函数的参数和返回值
当结构体作为函数参数时,并要在函数内部修改结构体成员的话,必须用指针传入.
go语言中,所有的参数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量.
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对象表示法,是一种用于接收和发送结构化信息的标准协议.
type student struct {Name stringAge, Grade intSchool 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是完全够的,但有时需要复杂打印格式,
{"editor.fontSize":18,"editor.suggestSelection": "first",// vscode默认启用了根据文件类型自动设置tabsize的选项"go.formatTool": "gofmt","editor.detectIndentation": false,"editor.formatOnSave": true,// 自动修复"editor.codeActionsOnSave": {"source.fixAll.eslint": "explicit"},...}