Golang基础-5
Golang基础-5
map
又叫映射(map),go语言中内置的一种类型,同样也是键值对关联
基本语法
var map变量名 map[keyType]valueType
注意key的数据类型不能是slice,map,function,一般是int,string类型
value的数据类型一般是数字(整数,浮点),string,map,结构体
如果只定义map,内存是不会分配其空间的
必须通过make函数进行初始化,才会分配空间

func main(){
// 定义map变量,如果只定义,内存是不会分配其空间的
// 必须通过make函数进行初始化,才会分配空间
var a map[int]string
a=make(map[int]string,10)//可以存放10个键值对
// 存入键值对
a[190501] = "张三"
a[190502] = "李四"
a[190503] = "王s"
a[190503] = "王五"
a[190504] = "王五"
// 输出集合
fmt.Println(a)
}

通过以上例子看出map的特点:
1.map集合在使用时一定要用make函数分配空间
2.map的key-value是无序的(你怎样存入顺序就咋样,不会排序)
3.key不能重复,如果重复,后面的value将会覆盖前面的value(相当于重新赋值),value是可以重复的
创建方式
var a map[int]string
a=make(map[int]string,10)//可以存放10个键值对
2.这种方式可以不指定map的长度
b:=make(map[int]string)
3.直接定义的时候初始化,每个后面都必须加‘ , ’
c := map[int]string{
190501 : "张三",
190502 :"李四",
}
操作
增加&修改
var a map[int]string
a=make(map[int]string,10)//可以存放10个键值对
// 增加存入键值对
a[190501] = "张三"
a[190502] = "李四"
a[190503] = "王s"
//修改
a[190503] = "王五"
// 输出集合
fmt.Println(a)
删除
通过内置函数delete()

func main(){
// 定义map变量,如果只定义,内存是不会分配其空间的
// 必须通过make函数进行初始化,才会分配空间
var a map[int]string
a=make(map[int]string,10)//可以存放10个键值对
// 存入键值对
a[190501] = "张三"
a[190502] = "李四"
a[190503] = "王s"
a[190503] = "王五"
a[190504] = "王五"
delete(a,190504)
// 输出集合
fmt.Println(a)
}

清空
go语言不像其他语言提供了清空的函数,要想清空一个map有两种方式
1.遍历map,逐个删除
2.map=make()make一个新的,让原来的称为垃圾,被gc回收
查找
value为对应key的value,bool为是否返回(有对应key返回true,没有为false)
value,bool=map[key]
获取长度
var m map[int]string
m=make(map[int]string,10)
l=len(m)
遍历
只支持for range遍历
var a map[int]string
a=make(map[int]string,10)//可以存放10个键值对
// 存入键值对
a[190501] = "张三"
a[190502] = "李四"
a[190503] = "王s"
a[190503] = "王五"
a[190504] = "王五"
delete(a,190504)
// 输出集合
for k,v:=range a{
fmt.Printf("a[%v]:%v\n",k,v)
}

当value的类型是map型的时候
b:=make(map[string]map[int]string)
// 赋值
b["1班"] = make(map[int]string,3)
b["1班"][1905101] = "小张"
b["1班"][1905103] = "小郭"
b["1班"][1905102] = "小王"
b["2班"] = make(map[int]string,3)
b["2班"][1905201] = "张三"
b["2班"][1905203] = "郭龙"
b["2班"][1905202] = "王菲"
for clas,s :=range b{
fmt.Println("班级:",clas)
for k,v:=range s{
fmt.Printf("学号:%v\t姓名:%v\n",k,v)
}
}

面向对象

其实继承的实现是通过匿名字段实现,多态的实现是通过接口来完成
结构体
具体对象:
一位学生:名字,年龄,性别,学号
在java中直接封装成一个学生类,但是go语言中没有类,只能通过结构体实现封装
结构体中可以参照java类有属性和特定的行为(方法)
实例创建
方式一:
type Student struct{
// 变量名字大写外界可以访问
Name string
Age int
Id int
Sex string
}
func main(){
//创建学生结构体的实例
var w Student
// 在未赋值时会给初始值给每个属性
fmt.Println(w)
w.Name="汪洋"
fmt.Println(w)
}

方式2:
var w Student=Student{}
w.Name="汪洋"
w.Age=21
//
var w Student=Student{"汪洋",21,1905300111,"男"}
方式三;
var s *Student=new(Student)
//s是指针类型的,实际指向的是一个个地址,应该给这个地址所存的对象进行赋值操作
(*s).Name = "张三"
//go对这种方式提供了简化的赋值方式,下面这样也是可以的
s.Age = 21
方式四:
var s *Student=&Student{}
(*s).Name = "张三"
//go对这种方式提供了简化的赋值方式,下面这样也是可以的
s.Age = 21
结构体之间的转化
前提:
转换时需要完全相同的字段名即里面的属性方法都要相同(名字,个数,类型)
匿名组合
匿名字段
用于实现继承,实现代码复用如下:
type Person struct{
name string
sex byte
age int
}
type Student struct{
Person//匿名字段,默认student就包含了Person的所有字段
id int
addr string
}
我们可以说student继承了person
以上对上面例子做一个实例并且初始化
func main() {
//创建学生结构体的实例
// 顺序初始化
var w Student = Student{Person{"wangyang", 'm', 22}, 1905011, "cd"}
// 在未赋值时会给初始值给每个属性
fmt.Println(w)
w.name = "汪洋"
fmt.Println(w)
// 详细初始化
s := Student{Person: Person{name: "mike"}, id: 167555, addr: "成都"}
// Printf中动词使用+v%显示更加详细
fmt.Printf("%+v", s)
}
运行如下

对成员的操作和java中类似直接通过 . 调用
// 成员操作
s.name = "小羊"
s.age = 22
s.sex = 1
s.id = 1905300111
fmt.Printf("%+v\n", s)

注意当结构体中的字段和其父类字段同名时,操作的默认是自身的字段
当然匿名字段也可以是非结构体(只是意义不大)

对这种成员的操作直接通过 .Type来进行
方法
方法和函数的区别在于其总是绑定一个类型
func (xxx Type) name(参数列表)(返回值列表){ 方法体}
注意这里的Type(接受者类型)本身类型(底层类型)不能是指针
type long int //long类型做接受者是合法的,且*long类型做接受者是合法的
type o *int //o类型做接受者是不合法的
调用方法的格式
对应Type的变量.方法name(传入参数)
以下通过写一个两数相加的函数和方法来说明其区别
函数:
func Add(a,b int) int{
return a+b
}
方法

注意只要接受者类型不一样,就算方法同名也是不一样的方法
值语义:接受者为普通变量,相当于拷贝,不会改变原有的值,如果要改变原有的值只能通过返回值改变
引用传递:接受者为指针类型,可以直接改变
type Person struct {
name string
sex byte
age int
}
//值语义
func (p Person) SetPerson(n string, s byte, a int) {
p.name = n
p.sex = s
p.age = a
}
//引用传递
func (person *Person) SetP(n string, s byte, a int) {
person.name = n
person.sex = s
person.age = a
}
s.age = 22
p := Person{}
(&p).SetP("引用传递", 'b', 2)
fmt.Printf("%+v\n", p)
p.SetPerson("值语义", 'a', 1)
fmt.Printf("%+v\n", p)

可以看到我们的值语义没有改变成功
下面我们给其添加返回值,再用原变量接收
//值语义
func (p Person) SetPerson(n string, s byte, a int) Person {
p.name = n
p.sex = s
p.age = a
return p
}
p = p.SetPerson("值语义", 'a', 1)

方法集
结构体变量是一个指针变量,它能够调用的所有方法,称之为一个方法集
结构体变量为指针时可以调用其接受者为指针和非指针的方法,非指针变量也可以调用接受者为指针和非指针的方法
同理非结构体变量也是可以调用对应的接受者为指针和非指针的方法
注意这里的接受者为指针和非指针的方法必须要对应,即p类型的变量只能调用接受者为p或*p的方法
方法的继承
继承都是使用匿名字段来实现,下面通过一个例子举例
type Person struct {
name string
sex byte
age int
}
type Student struct {
Person
id int
addr string
}
// Person类型,实现一个打印方法
func (p *Person) PersonInfo() {
fmt.Printf("name=%s\tsex=%c\tage=%d\n", p.name, p.sex, p.age)
}
func main() {
s := Student{Person{"张三", 'm', 22}, 1905301, "成都"}
s.PersonInfo()
}
这里就可以说是Student继承了Person且可以调用其绑定的方法

在上面的基础上我又定义了一个接受者不同的同名方法
//方法的重写
func (s *Student) PersonInfo() {
fmt.Printf("name=%s\tsex=%c\tage=%d\tid=%d\taddr=%s\n", s.name, s.sex, s.age, s.id, s.addr)
}
再次执行发现会优先调用该方法(即完成了方法的重写)

方法值
就是将接受者和方法名合在一起存入一个变量v中通过v()可以直接调用方法

方法表达式
就是将接受者的类型和方法名合并保存到变量x中,通过x(对应类型的变量)来调用方法

接口
在GO语言中接口是一个自定义类型,具体描述了一系列方法的集合
接口的定义
接口里面的方法是没有实现的(和java一样),只有方法名
通过具体的自定义类型实现
type MyStr string
type Humaner interface {
//声明方法()
sayHi()
}
type Person struct {
name string
sex byte
age int
}
type Student struct {
Person
id int
addr string
}
// Student对象实现接口中的方法
func (s *Student) sayHi() {
fmt.Printf("Student[%v]", s)
}
// 自定义类型MyStr实现接口中的方法
func (m *MyStr) sayHi() {
fmt.Printf("类型是%T\n", m)
}
func main() {
s := Student{Person{"张三", 'm', 22}, 1905301, "成都"}
// 定义一个接口类型的变量
var i Humaner
// 只要是实现了接口方法的类型就可以给接口变量赋值
var m MyStr
i = &m
i.sayHi()
i = &s
i.sayHi()
}

下面这种调用同一方法的不同表现就叫做多态,一般多态的实现-定义一个函数其参数为一个接口类型,函数体中调用接口的方法
// 定义一个函数,其参数为接口类型
func infac(h Humaner) {
h.sayHi()
}
s := Student{Person{"张三", 'm', 22}, 1905301, "成都"}
var m MyStr
infac(&m)
infac(&s)
//或者使用切片
r := make([]Humaner,2)
r[0] = &m
r[2] = &s
for _,value = range r{
value.sayHi()
}
接口的继承
同样也是通过匿名字段来实现
type Humaner interface {
//声明方法()
sayHi()
}
type zi interface {
Humaner//匿名字段继承了sayhi
//声明方法()
sing()
}
type Student struct{
name string
id int
}
//Student实现了sayhi
func (s *Student) sayHi() {
fmt.Printf("Student[%v]", s)
}
//Student实现了sing
func (s *Student) sing() {
fmt.Printf("Student[%v]sing", s)
}
func main(){
var i zi
s := &Student{Person{"张三", 'm', 22}, 1905301, "成都"}
i = s
i.sayHi()
i.sing()
}
上面就是zi继承了接口Humaner,如果自定义类型要实现接口就要实现所有的方法,包括父类接口的
注意子类可以给父类赋值,而父类不能给子类
即:
var i Humaner
var z zi
i=z//合法
z=i//报错
空接口
是一个万能类型,可以保存任意类型的值

类型断言
断言用于判断某变量v是否属于某类型type,返回值有 data-数据值 is-是否是该类型(布尔类型)
data,is := v.(type)
我们可以根据is来对该变量进行特定的操作
func main() {
// 创建一个切片
arr := make([]interface{}, 3)
arr[0] = 123
arr[1] = 3.1456
arr[2] = "hello"
for _, v := range arr {
// 对数据v进行类型断言
data, ok := v.(int)
if ok {
fmt.Println(data)
}
}
}

go语言中断言和if语句还可以写在一起


以上例子用于筛选判断切片中的各类型并给出特定的操作
通过switch来实现断言
switch value := v.(type) {
case int:
fmt.Println("整型数据:", value)
case string:
fmt.Println("字符串类型数据:", value)
case float64:
fmt.Println("浮点类型数据:", value)
}
}
其和上面的结果是一样的

浙公网安备 33010602011771号