九、Go语法基础(映射表(map))

40 阅读3分钟

Golang中文学习文档地址

映射表通常的两种实现方式:

  • 哈希表(hash table):无序
  • 搜索树(search tree):有序

在Go中,映射表的实现是基于哈希桶(是哈希表的一种实现),所以Go的映射表也是无序的。
在Go中,map 的键类型必须是可比较的,比如stringint是可比较的,而[]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来替代。