Last active
August 5, 2021 09:58
-
-
Save z1076/6292d6c18a2aabcfb360b2bb4e83a015 to your computer and use it in GitHub Desktop.
[集合 Map] #go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// vim: syntax=go | |
## 一 集合map | |
#### 1.1 map的创建 | |
Go内置了map类型,map是一个无序键值对集合(也有一些书籍翻译为字典)。 | |
普通创建: | |
```go | |
// 声明一个map类型,[]内的类型指任意可以进行比较的类型 int指值类型 | |
m := map[string]int{"a":1,"b":2} | |
fmt.Print(m["a"]) | |
``` | |
make方式创建map: | |
```go | |
type Person struct{ | |
ID string | |
Name string | |
} | |
func main() { | |
var m map[string] Person | |
m = make(map[string] Person) | |
m["123"] = Person{"123","Tom"} | |
p,isFind := m["123"] | |
fmt.Println(isFind) //true | |
fmt.Println(p) //{123 Tom} | |
} | |
``` | |
注意:golang中map的 key 通常 key 为 int 、string,但也可以是其他类型如:bool、数字、string、指针、channel,还可以是只包含前面几个类型的接口、结构体、数组。slice、map、function由于不能使用 == 来判断,不能作为map的key。 | |
#### 1.2 map的使用 | |
通过key操作元素: | |
```go | |
var numbers map[string]int | |
numbers = make(map[string]int) | |
numbers["one"] = 1 //赋值 | |
numbers["ten"] = 10 //赋值 | |
numbers["three"] = 3 | |
delete(numbers, "ten") // 删除key为 ten 的元素 | |
fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据 | |
``` | |
map的遍历:同数组一样,使用for-range 的结构遍历 | |
注意: | |
- map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取; | |
- map的长度是不固定的,也就是和slice一样,也是一种引用类型 | |
- 内置的len函数同样适用于map,返回map拥有的key的数量 | |
- go没有提供清空元素的方法,可以重新make一个新的map,不用担心垃圾回收的效率,因为go中并行垃圾回收效率比写一个清空函数高效很多 | |
- map和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制 | |
#### 1.3 并发安全的map | |
演示并发读写map的问题: | |
```go | |
package main | |
func main() { | |
m := make(map[int]int) | |
go func() { | |
for { //无限写入 | |
m[1] = 1 | |
} | |
}() | |
go func() { | |
for { //无限读取 | |
_ = m[1] | |
} | |
}() | |
for {} //无限循环,让并发程序在后台执行 | |
} | |
``` | |
编译会有错误提示:`fatal error: concurrent map read and map write`,即出现了并发读写,因为用两个并发程序不断的对map进行读和写,产生了竞态问题。map内部会对这种错误进行检查并提前发现。 | |
Go内置的map只有读是线程安全的,读写是线程不安全的。 | |
需要并发读写时,一般都是加锁,但是这样做性能不高,在go1.9版本中提供了更高效并发安全的sync.Map。 | |
sync.Map的特点: | |
- 无须初始化,直接声明即可 | |
- sync.Map不能使用map的方式进行取值和设值操作,而是使用sync.Map的方法进行调用。Store表示存储,Load表示获取,Delete表示删除。 | |
- 使用Range配合一个回调函数进行遍历操作,通过回调函数返回内部遍历出来的值,需要继续迭代时,返回true,终止迭代返回false。 | |
```go | |
package main | |
import ( | |
"fmt" | |
"sync" | |
) | |
func main() { | |
var scene sync.Map | |
//保存键值对 | |
scene.Store("id",1) | |
scene.Store("name","lisi") | |
//根据键取值 | |
fmt.Println(scene.Load("name")) | |
//遍历 | |
scene.Range(func(k, v interface{}) bool{ | |
fmt.Println(k,v) | |
return true | |
}) | |
} | |
``` | |
注意:map没有提供获取map数量的方法,可以在遍历时手动计算。sync.Map为了并发安全。损失了一定的性能。 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment