【Go】Go 语言 – Map(集合)

导读:本篇文章讲解 【Go】Go 语言 – Map(集合),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com


一、Map(集合)

1. Map 是集合

Map 是一种 无序键值对集合 。Map 最重要的一点是 通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。用 Go 语言写代码时,要用到哈希表时用 Map 。

2. Map 是哈希表

哈希表(Map )是一种巧妙并且实用的数据结构。它是一个无序的 key/value 对的集合,其中所有的 key 都是不同的,然后通过给定的 key 可以在常数时间复杂度内检索、更新或删除对应的 value。

在Go语言中,一个map就是一个哈希表的引用,map 类型可以写为 map[K]V,其中 K 和 V 分别对应 key 和 value。map 中所有的 key 都有相同的类型,所有的 value 也有着相同的类型,但是 key 和 value 之间可以是不同的数据类型。其中 K 对应的 key 必须是支持 == 比较运算符的数据类型,所以 map 可以通过测试 key 是否相等来判断是否已经存在。虽然浮点数类型也是支持相等运算符比较的,但是将浮点数用做 key 类型则是一个坏的想法,最坏的情况是可能出现的 NaN 和任何浮点数都不相等。对于 V 对应的 value 数据类型则没有任何的限制。

3. Map 是引用类型

Go语言中的 map 是引用类型,必须初始化才能使用。
为什么引用类型必须初始化才能使用?请参见【Go】Go语言中的 new 和 make 函数


二、声明 Map

可以使用内建函数 make 也可以使用 map 关键字来声明 Map:

//声明
var map_name map[key_type]value_type
//初始化或者说是创建
map_name = make(map[key_type]value_type)


//直接使用 make 函数进行声明+创建
map_name := make(map[key_type]value_type)

key_type表示键的数据类型,value_type表示键对应的值的数据类型。

map是引用类型,如果只声明,而不创建 map(用 make 函数创建map),那么就会创建一个 nil map。nil map 不能用来存放键值对,如果对nil map 进行操作会报错。

声明之后,map类型的变量默认初始值为 nil,需要使用 make() 函数来分配内存。语法为:

make(map[key_type]value_type, [cap])

其中 cap 表示 map 的容量,该参数虽然不是必须的,但是我们应该在初始化 map 的时候就为其指定一个合适的容量。

想了解引用类型的 “必须初始化” 具体深层原因请读【Go】Go语言中的 new 和 make 函数


三、初始化 Map

map 中的数据都是成对出现的。有以下两种初始化 map 的方式:

方式一:

//声明+初始化
map_name := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

方式二:

countryCapitalMap["France"] = "巴黎"
countryCapitalMap["Italy"] = "罗马"
countryCapitalMap["Japan"] = "东京"
countryCapitalMap["India "] = "新德里"

四、创建和使用 map 的一个简单实例

package main

import "fmt"

func main() {
	//声明+创建
	scoreMap := make(map[string]int, 8)
	//初始化值
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	
	fmt.Println(scoreMap)
	fmt.Println(scoreMap["小明"])
	fmt.Printf("type of a:%T\n", scoreMap)
}

输出结果:

map[小明:100 张三:90]
100
type of a:map[string]int

五、遍历 map

1. 直接遍历是无序的

Go语言中使用 for range 遍历 map:

package main

import "fmt"

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	scoreMap["王五"] = 60
	for k, v := range scoreMap {
		fmt.Println(k, v)
	}
}

输出结果:

张三 90
小明 100
王五 60

但我们只想遍历 key 的时候,可以按下面的写法:

package main

import "fmt"

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	scoreMap["王五"] = 60
	for k := range scoreMap {
		fmt.Println(k)
	}
}

输出结果:

王五
张三
小明

总结:

  1. 如果用 range 遍历 Map 时,range 会按顺序返回两个值:键和值。
    如果只有一个接收者的话,默认接收的是键(key)。
  2. 遍历 map 时的元素顺序与添加键值对的顺序无关。

2. 有序遍历

要实现 map 的有序遍历,需要对键进行排序,再按照键的顺序输出值。

对键进行排序的方法是把所有的键放在一个切片里,然后用sort包中的函数进行排序。

package main

import (
	"fmt"
	"math/rand"
	"sort"
	"time"
)

func main() {
	rand.Seed(time.Now().UnixNano()) //初始化随机数种子

	var scoreMap = make(map[string]int, 100)

	for i := 0; i < 50; i++ {
		key := fmt.Sprintf("stu%02d", i) //生成stu开头的字符串
		value := rand.Intn(50)           //生成0~99的随机整数
		scoreMap[key] = value
	}
	fmt.Println("直接遍历:")
	for key := range scoreMap {
		fmt.Println(key, scoreMap[key])
	}

	//取出map中的所有key存入切片keys
	var keys = make([]string, 0, 100)
	for key := range scoreMap {
		keys = append(keys, key)
	}
	//对切片进行排序
	sort.Strings(keys)
	//按照排序后的key遍历map
	fmt.Println("有序遍历:")
	for _, key := range keys {
		fmt.Println(key, scoreMap[key])
	}
}

输出结果:

直接遍历:
stu17 12
stu18 17
stu43 35
stu13 18
stu19 16
stu35 31
stu47 12
stu16 0
stu20 1
stu37 32
stu48 24
stu42 42
stu02 4
stu09 16
stu21 5
stu25 48
stu32 15
stu41 49
stu00 35
stu08 2
stu11 22
stu45 10
stu33 41
stu49 4
stu28 31
stu46 26
stu01 19
stu22 17
stu10 39
stu15 27
stu30 38
stu03 25
stu04 14
stu23 21
stu44 45
stu05 45
stu26 46
stu34 25
stu36 1
stu39 11
stu07 32
stu06 16
stu24 25
stu27 3
stu31 5
stu40 6
stu12 36
stu14 15
stu29 26
stu38 44
有序遍历:
stu00 35
stu01 19
stu02 4
stu03 25
stu04 14
stu05 45
stu06 16
stu07 32
stu08 2
stu09 16
stu10 39
stu11 22
stu12 36
stu13 18
stu14 15
stu15 27
stu16 0
stu17 12
stu18 17
stu19 16
stu20 1
stu21 5
stu22 17
stu23 21
stu24 25
stu25 48
stu26 46
stu27 3
stu28 31
stu29 26
stu30 38
stu31 5
stu32 15
stu33 41
stu34 25
stu35 31
stu36 1
stu37 32
stu38 44
stu39 11
stu40 6
stu41 49
stu42 42
stu43 35
stu44 45
stu45 10
stu46 26
stu47 12
stu48 24
stu49 4

六、判断某个键是否存在

Go语言中有个判断 map 中某个键是否存在的特殊写法,格式如下:value, ok := map[key]
如果键存在,那么会返回 相应的值true
如果键不存在,那么会 false

package main

import "fmt"

func main() {
	//声明
	var countryCapitalMap map[string]string
	//创建
	countryCapitalMap = make(map[string]string)

	// map插入key - value对,各个国家对应的首都
	countryCapitalMap["France"] = "巴黎"
	countryCapitalMap["Italy"] = "罗马"
	countryCapitalMap["Japan"] = "东京"
	countryCapitalMap["India "] = "新德里"

	/*使用键输出地图值
	for country, value := range countryCapitalMap {
		fmt.Println(country, "首都是", value)
	} */

	//使用键输出地图值的另一种方法:
	for country := range countryCapitalMap {
		fmt.Println(country, "首都是", countryCapitalMap[country])
	}
	//也就是说,如果用range遍历Map而只有一个接收者的话,默认接收的是键(key)

	//查看元素在集合中是否存在
	capital, ok := countryCapitalMap["American"] /*如果确定是真实的,则存在,否则不存在 */

	fmt.Println(capital)
	fmt.Println(ok)

	if ok {
		fmt.Println("American 的首都是", capital)
	} else {
		fmt.Println("American 的首都不存在")
	}

	capital, ok = countryCapitalMap["France"] /*如果确定是真实的,则存在,否则不存在 */

	fmt.Println(capital)
	fmt.Println(ok)

	if ok {
		fmt.Println("France 的首都是", capital)
	} else {
		fmt.Println("France 的首都不存在")
	}
}

输出结果:

France 首都是 巴黎
Italy 首都是 罗马
Japan 首都是 东京
India  首都是 新德里

false
American 的首都不存在
巴黎
true
France 的首都是 巴黎

七、delete() 函数

delete() 函数用于删除集合 map 中的元素, 参数为某个 map 和其中的某个 key。实例如下:

使用 delete() 内建函数从 map 中删除一组键值对,delete() 函数的格式如下:

delete(map, key)

其中:
map表示要删除键值对的map,key表示要删除的键值对的键

实例 1 :

package main

import "fmt"

func main() {
	scoreMap := make(map[string]int)
	scoreMap["张三"] = 90
	scoreMap["小明"] = 100
	scoreMap["王五"] = 60
	delete(scoreMap, "小明") //将小明:100从map中删除
	for k, v := range scoreMap {
		fmt.Println(k, v)
	}
}

输出结果:

王五 60
张三 90

实例 2 :

package main

import "fmt"

func main() {
	/* 创建map */
	countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}

	fmt.Println("原始地图")

	/* 打印地图 */
	for country := range countryCapitalMap {
		fmt.Println(country, "首都是", countryCapitalMap[country])
	}

	/*删除元素*/
	delete(countryCapitalMap, "France")
	fmt.Println("法国条目被删除")

	fmt.Println("删除元素后地图")

	/*打印地图*/
	for country := range countryCapitalMap {
		fmt.Println(country, "首都是", countryCapitalMap[country])
	}
}

输出结果:

原始地图
India 首都是 New delhi
France 首都是 Paris
Italy 首都是 Rome
Japan 首都是 Tokyo
法国条目被删除
删除元素后地图
India 首都是 New delhi
Italy 首都是 Rome
Japan 首都是 Tokyo

八、复杂数据类型

1. 元素为 map 类型的切片

下面的代码演示了切片中的元素为map类型时的操作:

package main

import (
	"fmt"
)

func main() {
	// mapSlice是一个元素为map类型的切片
	var mapSlice = make([]map[string]string, 3)
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
	fmt.Println("after init")
	// 对切片中的map元素进行初始化
	mapSlice[0] = make(map[string]string, 10)
	mapSlice[0]["name"] = "王五"
	mapSlice[0]["password"] = "123456"
	mapSlice[0]["address"] = "红旗大街"
	for index, value := range mapSlice {
		fmt.Printf("index:%d value:%v\n", index, value)
	}
}

输出结果:

index:0 value:map[]
index:1 value:map[]
index:2 value:map[]
after init
index:0 value:map[address:红旗大街 name:王五 password:123456]
index:1 value:map[]
index:2 value:map[]

2. 值为切片类型的 map

下面的代码演示了 map 中值为切片类型的操作:

package main

import (
	"fmt"
)

func main() {
	//声明+创建 map,这个map的键是string类型,值是[]string切片类型
	var sliceMap = make(map[string][]string, 3)
	fmt.Println(sliceMap)

	fmt.Println("after init")
	key := "中国"
	value, ok := sliceMap[key]
	if !ok { //如果sliceMap中不存在这个键
		value = make([]string, 0, 2)
	}
	value = append(value, "北京", "上海")
	sliceMap[key] = value
	fmt.Println(sliceMap)
}

输出结果:

map[]
after init
map[中国:[北京 上海]]

参考链接

  1. Go 语言Map(集合)
  2. Map

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/119034.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!