文章目录
-
- 23. map
- 24 对于 slice、map等需要分配内存空间的,使用之前一定要判断一下是否为空
- 25 线程同步的包sync
- 26 原子操作
- 27 结构体
-
- 27.1 通过结构体指针访问结构体成员
- 27.2 结构体中的所有字段在内存中都是连续的
- 27.3 结构体中的成员的访问权限控制
- 27.4 二叉树
- 27.5 工厂模式
- 27.5 struct中的tag
- 27.6 匿名字段
- 27.7 方法
- 27.8 go里面通过匿名字段实现继承
- 27.9 多重继承
- 27.10 接口
- 27.11 interface关键字
- 27.12 接口嵌套
- 27.13 类型断言
- 27.14 反射
- 27.15 获取 `ValueOf` 返回值的具体值
- 27.16 对 ValueOf() 返回值使用 SetXxx() 方法
- 27.17 反射操作结构体
- 27.18 模仿 `encoding/json` 获取结构体tag
- 27.19 均衡负载案例
- 28 go的类型很严格
- 29 读写
- 30 命令行参数
- 31 json协议
- 32 错误处理
- 33 设置goroute运行在多少个核上
- 34 定时器
- 35 单元测试
23. map
key-value 的数据结构,又叫字典或关联数组
23.1 map的声明
声明格式如下,声明的map需要使用make分配内存空间,也可以声明的时候初始化。
package main
import (
"fmt"
)
func main() {
//var mapName map[keyType]valueType
var map1 map[string]string
var map2 map[string]int
var map3 map[int]string
var map4 map[string]map[string]string
fmt.Println(map1)
fmt.Println(map2)
fmt.Println(map3)
fmt.Println(map4)
}
/*
output:
API server listening at: 127.0.0.1:47828
map[]
map[]
map[]
map[]
Process exiting with code: 0
*/
23.2 make给map分配内存空间
package main
import (
"fmt"
)
func main() {
//var mapName map[keyType]valueType
var map2 map[string]int
map2 = make(map[string]int, 3)
map2["xiaoming"] = 22
map2["xiaowang"] = 23
map2["xiaoyao"] = 25
fmt.Println(map2)
}
/*
output:
API server listening at: 127.0.0.1:18955
map[xiaoming:22 xiaowang:23 xiaoyao:25]
Process exiting with code: 0
*/
23.3 map声明的时候初始化
package main
import (
"fmt"
)
func main() {
//var mapName map[keyType]valueType
var map2 map[string]int = map[string]int{
"xiaoming": 22, "xiaowang": 23, "xiaoyao": 25}
fmt.Println(map2)
}
/*
output:
API server listening at: 127.0.0.1:16196
map[xiaoming:22 xiaowang:23 xiaoyao:25]
Process exiting with code: 0
*/
23.4 map的增删改查
备注:想清空 map 只能使用 23.5 中的 for 循环,或者重新 make 一下
package main
import (
"fmt"
)
func main() {
//var mapName map[keyType]valueType
var map2 map[string]int
map2 = make(map[string]int, 3)
fmt.Println(map2)
map2["xiaoyao"] = 25 //增
fmt.Println(map2)
fmt.Println(map2["xiaoyao"])//查
map2["xiaoyao"] = 23 //改 在key"xiaoyao"已经存在的情况下,重新赋值完成了改操作
fmt.Println(map2["xiaoyao"])
delete(map2, "xiaoyao") //删
fmt.Println(map2)
fmt.Println(len(map2)) //求长度
}
/*
output:
API server listening at: 127.0.0.1:38566
map[]
map[xiaoyao:25]
25
23
map[]
0
Process exiting with code: 0
*/
23.5 map的遍历
package main
import "fmt"
func main() {
//var mapName map[keyType]valueType
var map2 = map[string]int{
"xiaoyao": 25, "xiaowang": 27, "xiaolei": 30}
fmt.Println(map2)
for key, value := range map2 {
fmt.Println(key, " = ", value)
}
}
/*
output:
API server listening at: 127.0.0.1:29281
map[xiaolei:30 xiaowang:27 xiaoyao:25]
xiaoyao = 25
xiaowang = 27
xiaolei = 30
Process exiting with code: 0
*/
23.6 map数据查询
根据key查询value是否存在,不能直接使用 var value = mapName[key]
的形式,因为假如 mapName[key]
不存在,它会发回给你一个初始值,而初始值并不能证明该值存在与否。
package main
import "fmt"
func main() {
//var mapName map[keyType]valueType
var map2 = map[string]int{
"xiaoyao": 25, "xiaowang": 27, "xiaolei": 30}
fmt.Println(map2)
var value = map2["xiaojia"]
fmt.Println("map2[\"xiaojia\"] = ", value)
}
/*
output:
API server listening at: 127.0.0.1:12698
map[xiaolei:30 xiaowang:27 xiaoyao:25]
map2["xiaojia"] = 0
Process exiting with code: 0
*/
查询map数据的正确姿势:
package main
import "fmt"
func main() {
//var mapName map[keyType]valueType
var map2 = map[string]int{
"xiaoyao": 0, "xiaowang": 27, "xiaolei": 30}
fmt.Println(map2)
value1, ok1 := map2["xiaojia"]
if ok1 {
fmt.Println("map2[\"xiaojia\"] = ", value1)
} else {
fmt.Println("map2[\"xiaojia\"] doesn't exist.")
}
value2, ok2 := map2["xiaoyao"]
if ok2 {
fmt.Println("map2[\"xiaoyao\"] = ", value2)
} else {
fmt.Println("map2[\"xiaoyao\"] doesn't exist.")
}
}
/*
output:
API server listening at: 127.0.0.1:49476
map[xiaolei:30 xiaowang:27 xiaoyao:0]
map2["xiaojia"] doesn't exist.
map2["xiaoyao"] = 0
Process exiting with code: 0
*/
24 对于 slice、map等需要分配内存空间的,使用之前一定要判断一下是否为空
package main
import "fmt"
func main() {
//var mapName map[keyType]valueType
var map2 map[string]int
if map2 == nil {
map2 = make(map[string]int, 3)
}
fmt.Println(map2)
}
/*
output:
API server listening at: 127.0.0.1:19465
map[]
Process exiting with code: 0
*/
25 线程同步的包sync
25.1 锁的初步使用
锁分为读锁和写锁,这方面的资料有待补充,写好之后会填充一个链接在这里
使用go build编译时,使用 --race
选项,可以使程序在编译完成后,执行时,如果存在资源竞争,则会报出来。
package main
import (
"math/rand"
"sync"
)
var lock sync.Mutex
func testMap() {
var a map[int]int
a = make(map[int]int, 10)
for i := 0; i < 10; i++ {
a[i] = i
}
for i := 0; i < 3; i++ {
go func(b map[int]int) {
lock.Lock()
b[0] = rand.Intn(100)
lock.Unlock()
}(a)
}
}
func main() {
testMap()
}
26 原子操作
go提供了原子操作相关的包:sync/atomic
package main
import (
"fmt"
"sync/atomic"
"time"
)
var count int32
func testAtomic() {
for i := 0; i < 10000; i++ {
go func() {
atomic.AddInt32(&count, 1)
//count++
}()
}
}
func main() {
testAtomic()
time.Sleep(3 * time.Second)
fmt.Println(count)
}
/*
output:
此时无论执行多少次,输出结果都是10000
但是如果不使用原子操作,而是直接count++
多次执行,几乎每次输出结果都小于10000
*/
27 结构体
- 用来自定义复杂数据结构
- struct 里面可以包含多个字段(属性)
- struct 类型可以定义方法,注意和函数区分
- struct 可以嵌套
- Go语言没有class,只有struct
定义语法:type structName struct { attr1 type1 attr2 type2 ... }
27.1 通过结构体指针访问结构体成员
pname.attr1 或者 (*pname).attr1,标准访问形式应该是第二种,但是go做了优化,第一种也可以。
27.2 结构体中的所有字段在内存中都是连续的
27.3 结构体中的成员的访问权限控制
和包的权限控制一样,结构体首字母大写的可以在包外部直接访问,首字母小写的无法在包外部访问。
27.4 二叉树
//TODO:此处资料待补充
27.5 工厂模式
go语言中struct没有构造函数,如果想要解决这个问题,可以使用工厂模式,专门为这个结构体创建一个初始化函数。
27.5 struct中的tag
我们可以为struct中的每一个字段写上一个tag,这个tag可以通过反射的机制来获取,最常用的场景就是json的序列化和反序列化。
package main
import (
"encoding/json"
"fmt"
)
type Student struct {
Name string //`json:"name"`
Age uint //`json:"age"`
}
func main() {
var stu Student = Student{
"xiaoyao",
18,
}
data, err := json.Marshal(stu)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data))
}
/*
output:
API server listening at: 127.0.0.1:32963
{"name":"xiaoyao","age":18}
Process exiting with code: 0
*/
27.6 匿名字段
同类型的匿名字段只能出现一个
访问匿名结构体字段时,可以直接越过结构体字段,直接使用点访问结构体成员内部的字段
访问匿名的基础类型字段时,可以通过 structName.type
的方式访问
下面是示例:
package main
import (
"fmt"
)
type Score struct {
math uint
English uint
}
type Student struct {
Name string
Age uint
int
Score
}
func main() {
var stu Student
stu.Name = "xiaoyao"
//stu.Score.math = 100
stu.math = 100 //实际是上一种写法的简化
stu.int = 175
fmt.Println(stu)
}
/*
output:
API server listening at: 127.0.0.1:13300
{xiaoyao 0 175 {100 0}}
Process exiting with code: 0
*/
27.7 方法
定义格式:
func (receiver type) methodName (paramsList)(returnValuesType) {
} //method1
func (receiver *type) methodName (paramsList)(returnValuesType) {
} //method2
上面两种写法中,method1的修改不会生效,method2的修改才会生效
下面是示例代码:
package main
import (
"fmt"
)
type Student struct {
Name string
Age uint
Score uint
}
func (stu Student) modifyName(newName string) {
stu.Name = newName
}
func (stu *Student) modifyAge(newAge uint) {
stu.Age = newAge
}
func main() {
var stu Student
stu.Name = "xiaoyao"
stu.Age = 25
stu.Score = 60
stu.modifyName("xiaoxiaoyao")
//(&stu).modifyAge(18)
stu.modifyAge(18)
/*
最符合调用规则的写法其实是被注释的那种写法
由于go的这种特性,跟其它语言相比有些反人类
所以go做了一些优化,直接使用结构体变量而不是结构体变量的地址也可以调用该方法
*/
fmt.Println(stu)
}
/*
output:
API server listening at: 127.0.0.1:27542
{xiaoyao 18 60}
Process exiting with code: 0
*/
27.8 go里面通过匿名字段实现继承
属性的继承上面已经写过,下面演示一下方法的继承。
子类指针可以直接访问父类的方法。
package main
import (
"fmt"
)
type people struct {
height uint
weight uint
}
type man struct {
people
name string
}
func (p *people) growUp() {
p.height++
p.weight++
}
func main() {
var xiaoyao man = man{
people{
175,
81,
},
"xiaoyao",
}
fmt.Println(xiaoyao)
xiaoyao.growUp()
fmt.Println(xiaoyao)
}
/*
output:
API server listening at: 127.0.0.1:21367
{
{175 81} xiaoyao}
{
{176 82} xiaoyao}
Process exiting with code: 0
*/
27.9 多重继承
多个匿名字段构成多继承
27.10 接口
广义上讲,接口是抽象出来的某种规范。其最大的意义在于将算法和实现进行分离,使得算法设计者可以不必再关心操作对象的具体实现。
在大多数编程语言中,接口是某个具体名称的函数。
例如下面的代码,函数max返回 a 和 b 中的较大者,这个函数中用到了比较操作,将比较操作抽象出一个接口,那么所有实现了比较操作这个接口的类型都可以使用此函数,具体的实现,各种编程语言有所不同,C++中使用的是模板,而Go语言中有其它方法实现,在学习了Go语言的接口之后,读者自己就会明白了,此处不多做赘述。
returnValue max(typeName a, typeName b) {
if (a > b) {
return a
}
return b
}
fmt.Printf(obj Object)
在打印时,会自动调用obj.String()来填充到 %s
部分,因此只要我们实现了 String()
这个函数,我们就能使用 fmt.Printf() 这个函数来打印我们的自定义类型。
下面是代码示范:
package main
import (
"fmt"
)
type people struct {
height uint
weight uint
}
type man struct {
people
name string
}
func (p *people) growUp() {
p.height++
p.weight++
}
func (p &man) String() string {
str := fmt.Sprintf("name = %s,\theight = %d, \tweight = %d", p.name, p.height, p.weight)
return str
}
func main() {
var xiaoyao man = man{
people{
175,
81,
},