映射表通常的两种实现方式:
- 哈希表(hash table):无序
- 搜索树(search tree):有序
在Go中,映射表的实现是基于哈希桶(是哈希表的一种实现),所以Go的映射表也是
无序的。
在Go中,map 的键类型必须是可比较的,比如string,int是可比较的,而[]int是不可比较的,也就无法作为 map 的键。
1、Map的初始化
映射表(Map)的两种初始化方式:
- 字面量初始化
import "fmt"
func main() {
map1 := map[int]string{
2: "B",
4: "C",
}
fmt.Println(map1)
}
make关键字初始化
import "fmt"
func main() {
map1 := make(map[string]int, 2)
map1["a"] = 1
map1["b"] = 2
fmt.Println(map1)
}
2、Map的访问
- 通过像数组索引类似的方式访问映射表元素,只是[]里面是key的值,而不是数组下标值。
func main() {
map1 := make(map[string]int, 2)
map1["a"] = 1
map1["b"] = 2
v,exist := map1["b"] //v:存储的值,exist:判断该值是否存在
fmt.Println(map1["a"]) //[]的值是映射表的键值
fmt.Println(map1["c"]) //当键所对应的值不存在时,输出的是对应类型的默认值
}
3、Map的存值
- 往map里面存值
import (
"fmt"
"math"
)
func main() {
map1 := make(map[float64]int, 2)
map1[1] = 1
map1[2] = 2
fmt.Println(map1[1])
map1[1] = 3 //会覆盖旧值
fmt.Println(map1[1])
map1[math.NaN()] = 4
map1[math.NaN()] = 5
fmt.Println(map1[math.NaN()]) //不会覆盖,无法正常取值,也无法判断是否键存在
fmt.Println(map1)
//输出
//1
//3
//0,无法正常取值
//map[NaN:4 NaN:5 1:3 2:2]
}
- NaN 是 IEE754 标准所定义的,其实现是由底层的汇编指令
UCOMISD完成,这是一个无序比较双精度浮点数的指令,该指令会考虑到 NaN 的情况,因此结果就是任何数字都不等于 NaN,NaN 也不等于自身,这也造成了每次哈希值都不相同。
4、删除
- Go使用
delete函数删除元素。
import "fmt"
func main() {
map1 := make(map[float64]int, 2)
map1[1] = 1
map1[2] = 2
fmt.Println(map1)
delete(map1, 1)
fmt.Println(map1)
}
5、遍历
- 使用
for range遍历
import (
"fmt"
"math"
)
func main() {
map1 := make(map[float64]int, 2)
map1[1] = 1
map1[2] = 2
map1[3] = 3
map1[4] = 4
map1[5] = 5
map1[math.NaN()] = 6
map1[math.NaN()] = 7
for key, v := range map1 {
fmt.Println(key, v)
}
//4 4
//5 5
//NaN 6
//NaN 7
//1 1
//2 2
}
6、清空
- 清空映射表内容,使用
clear函数
import (
"fmt"
)
func main() {
map1 := make(map[float64]int, 2)
map1[1] = 1
map1[2] = 2
map1[3] = 3
map1[4] = 4
map1[5] = 5
fmt.Println(map1)
clear(map1)
fmt.Println(map1)
}
7、Set
- Set 是一种无序的,不包含重复元素的集合,Go 中并没有提供类似的数据结构实现,但是 map 的键正是无序且不能重复的,所以也可以使用 map 来替代 set。
set := make(map[int]struct{}, 10)
for i := 0; i < 10; i++ {
set[i] = struct{}{}
}
for i := range set {
fmt.Println(i) //每次执行,输出都顺序都不一样。
}
8、map并发安全问题
- map 并不是一个并发安全的数据结构,Go 团队认为大多数情况下 map 的使用并不涉及高并发的场景,引入互斥锁会极大的降低性能。
- 如需要并发场景下使用,使用
sync.Map来替代。