初心是记录和总结,自己学习Go语言的历程。如果能帮助到你,这是我的荣幸。
map
Map的概念在任何程序语言都一样,存储key-value
这样的内容,通过key
可以查到对应的value
,key
是唯一的。
语法:var map[key的类型]value的类型
例子:
//定义一个map,这时候没有初始化是不能直接使用的
var province map[string]string
province["ZheJiang"] = "浙江" //报错
未初始化的Map赋值会报错:panic: assignment to entry in nil map
,说我们正在操作一个空的Map
panic
指的是Go语言遇到一个异常时候强制执行让程序终止的操作
map初始化的几种方法
- 使用
make
函数 - 使用
new
定义一个指针,然后使用*
解析地址赋给定义的map - 定义时同时赋值
//初始化方式一:使用make进行创建,初始指定容量为2
province = make(map[string]string, 2)
province["ZheJiang"] = "浙江"
province["ShangHai"] = "上海"
province["BeiJing"] = "北京"
// 初始化方式二:new返回的是一个指针
var province map[string]string
pointMap := new(map[string]string) // 返回的是一个map指针,指针是存地址的
province = *pointMap // *号的作用是解析地址
province["ZheJiang"] = "浙江"
province["ShangHai"] = "上海"
province["BeiJing"] = "北京"
// 我们手动给它初始化了,也是可以的
province := map[string]string{"JiangSu": "江苏", "SiChuan": "四川"}
查询map中是够包含某个键
基于map名[key名]
的方式,会返回value
和一个bool
类型,通过bool
可以判断是否含有这个键
_, ok := province["ZheJiang"] //_只是一个占位符
fmt.Println(ok) //true
value, ok := province["ZheJiang"]
fmt.Println(value) //浙江
fmt.Println(ok) //true
遍历整个map
通过range
的方式遍历Map,可以获得map的key
和value
。单独获取key
和value
,我们可以通过_
我们可以舍去不需要的数据来实现,示例代码如下:
// 遍历Map
for key, value := range province {
fmt.Println(key, value)
}
遍历Key
// 遍历Key
for key,_ := range province {
fmt.Println(key)
}
遍历Value
// 遍历value
for _, value := range province {
fmt.Println(value)
}
删除Map中的元素
使用内置函数delete
,通过key
删除map元素
delete(province, "ShangHai")
type关键字
使用type
我们可以自定义类型。这很有必要,因为我们需要描述一件事的事情的时候是需要描述很多内容。就像人,这个人会有年龄,名字。我们需要把人的概念抽出来,然后就可以用人来形容世界上所有的人,因为我们将年龄,名字这种共性的东西抽出来,形成人的概念了。而go语言中并没有人
这个变量类型,只有int
可以表示年龄,string
可以表示名字。如何定义人这个类型呢?使用type
和struct
,先了解一下type
。
语法:
我们可以自定义类型,使用go语言提供type
关键字,例如:
type myInt int
type是定义了一个全新的类型。我们可以基于内置的基本类型定义,上面的例子就是基于int
定义的新类型myInt
。现在我们准备回答人
这个类型怎么定义,通过type + struct。
struct 结构体
结构体有点类似于Java定义的类,里面有成员变量和方法。恰恰,在Go语言中方法就是一个类型,所以这和Java的类很像!,这样有助于我们开始理解struct
。拿个例子,定义一个person
类型,它可以描述人的年龄和姓名。
语法:
type 类型名 struct {
字段名 字段类型
字段名 字段类型
...
}
解读:type
是用来表示自定义类型的,struct
可以帮助我们使用多个字段类型来组成类型名
这个类型,其实我们就是在定义一个复合类型,这个类型的名字最终交类型名
。
定义person类型
:
type person struct{
name string //描述姓名
age int //描述年龄
}
定义完成之后,我们来定义一个person
类型的变量取名为people
,当输出people
的时候,我们会发现该类型中包含的类型都被自动初始化了。
var people person
fmt.Println(people) //{ 0}
定义的变量我们可以通过.
的方式访问到结构体中的字段名,并给与一些赋值的操作。
//通过类型.字段名的方式结构体的字段
people.name = "chengyunlai"
people.age = 18
fmt.Println(people) // {chengyunlai 18}
使用new的方式定义
我们使用new
的方式定义的时候,我们知道其实返回的是一个指针,所以people2
其实是一个person
类型的指针,里面存放的是地址,当我们打印的时候可以发现前面是带了个&
符号。
那我们当然知道*
是解析地址的作用,在前面加了个*
,发现打印出来就是实际的值
// 定义2 使用new返回指针的方式定义
people2 := new(person)
fmt.Println(people2) //&{ 0}
fmt.Println(*(people2)) //{ 0}
fmt.Println((*people2).age) // 0
其实不用这么麻烦,Go语言为我们准备了语法糖,看代码
people2.age = 18 // 相当于(*people2).age = 18
fmt.Println(people2.age)
结构体里放方法?
学过java的人,又了解过go语言中方法其实是一个类型的时候,会很开心的想到在结构体里放方法,比如说设置一个setter
方法,通过方法传参的方式设置name
和age
type person struct {
name string //描述姓名
age int //描述年龄
setNameAndAge func(p *person, name string, age int)
}
这个需要用到指针的知识,小伙伴们可以自己尝试,我就不挖坑了,因为我试过了。指针传递过来的地址保证了我们操作的是同一个地址。
解读:
这里我们定义了一个方法setName func(p *person, name string, age int)
,方法名为setName
,因为go
没有this的用法,所以我们把对谁
操作,写进了第一个变量中,我们需要做的是给name
和int
赋值,所以还定义了两个传入的参数。
使用:
var people person //定义person类型的变量为people
people.setNameAndAge = func(p *person, name_ string, age_ int) {
p.name = name_
p.age = age_
fmt.Printf("%p\n", p) //0xc0000543c0
}
people.setNameAndAge(&people, "张三", 15)
fmt.Printf("%p\n", &people) //0xc0000543c0
fmt.Println(people) //{张三 15 0xa4fc80}
解读:
由于我尝试在结构体中写方法,但是失败了,失败的原因看语法就能明白,里面只能定义。所以只能实例化(var people person
),像people.字段名
的方法进行方法的赋值(确实感觉笨笨的~),当然我们可以考虑将方法抽出来这样将方法名赋值就行。后面我们会说一下go语言应对这种情况的解决办法。但是我这个思想还是挺重要的。
people.setNameAndAge(&people, "张三", 15)
传入了操作的地址,以及我想赋值的变量值,由于我的方法已经定义好了,在方法中我特地打印了一下地址,可以发现两个打印输出的地址是一样的,这就是指针的作用。
最后输出了fmt.Println(people) //{张三 15 0xa4fc80}
发现尝试成功!