从0开始学Go(二)

文章目录

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,
		},
		"xiaoyao"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值