内容简介:我们来试着创建:
Hash
表是一种巧妙并且实用的数据结构,是一个无序的 key/value
对的集合,其中所有的 key
都是不同的,通过给定的 key
可以在 常数时间复杂度 内检索、更新或删除对应的value。 Map
其实是一个Hash表的引用,能够基于键快速检索出数据,键就像索引一样指向与该键关联的值。以后有机会再给大家讲 Map
底层的东西,教会大家如何使用 Map
才是这一节的重点,记住一点: Map
存储的是无序的键值对集合 。
创建与初始化
使用 make
函数
make
可以创建切片,也可以用来创建 Map
。规则是这样的:
m := make(map[keyType]valueType) 复制代码
我们来试着创建:
month := make(map[string]int) month["January"] = 1 month["February"] = 2 month["March"] = 3 复制代码
第一行代码,创建了 key
类型为 string
, value
类型为 int
的空 Map
month
,接着,给 month
赋值了三个键值对。
使用字面量
上面的代码,可以采用字面量的方式实现:
month := map[string]int{"January":1,"February":2,"March":3} // 还可以写成这样 month := map[string]int{ "January":1, "February":2, "March":3, } 复制代码
使用字面量也可以创建空 Map
,大括号里面不赋值就可以了:
month := map[string]int{} fmt.Println(month) // 输出:map[] 复制代码
有空 Map
,是不是有 nil
Map
?当然是有为 nil
的 Map
:
var month map[string]int fmt.Println(month == nil) // 输出:true 复制代码
对于 nil
的 map
是不能存取键值对的,否则就会报错 panic: assignment to entry in nil map
。可以使用提到的 make
函数,为其初始化:
var month map[string]int month = make(map[string]int) month["January"] = 1 fmt.Println(month) // 输出:map[January:1] 复制代码
自然能想到, Map
的零值就是 nil
, Map
就是底层 Hash
表的引用。 Map
的 key
可以是 内置类型 ,也可以是 结构类型 ,只要可以使用 ==
运算符做比较,都可以作为 key
。 切片 、 函数 以及 包含切片的结构类型 ,这些类型由于具有引用语义,不能作为 key
,使用这些类型会造成编译错误:
month := map[[]string]int{} // 编译错误:invalid map key type []string 复制代码
对于 Map
的 value
来说,就没有类型限制,当然也没有任何理由阻止用户使用切片作为 Map
的值:
m := map[string][]int{} slice := []int{1,2,3} m["slice"] = slice fmt.Println(m["slice"]) // 或者 slice := []int{1,2,3} m := map[string][]int{"slice":slice} fmt.Println(m["slice"]) 复制代码
第一段代码,创建了 key
类型为 string
,值为 slice
类型的空 Map
,接着将切片 slice
赋值给了名为 slice
的 key
。第二段代码是第一段代码的简写版本。
如何使用 Map
Map
的使用就很简单了,类似于数组,数组是使用索引, Map
使用 key
获取或修改 value
。
m := map[string]int{} m["January"] = 1 // 赋值 fmt.Println(m) // 输出:map[January:1] m["January"] = 10 //修改 fmt.Println(m) // 输出:map[January:10] january := m["January"] // 获取value fmt.Println(january) // 输出:10 复制代码
执行修改操作的时候,如果 key
已经存在,则新值会覆盖旧值,上面代码已经体现出来了,所以 key
是不允许重复的。 获取一个不存在的 key
的 value
的时候,会返回值类型对应的零值,这个时候,我们就不知道是存在一个值为零值的键值对还是键值对就根本不存在。好在, Map
给我们提供了方法:
february,exists := m["February"] fmt.Println(february,exists) // 输出:0 false 复制代码
获取值的时候多了一个返回值,第一个返回值是 value
,第二个返回值是 boolean
类型变量,表示 value
是否存在。这给我们判断一个 key
是否存在就提供了很大便利。
delete
--删除键值对
不像 Slice
, Go
为我们提供了删除键值对的功能-- delete
函数。 函数原型:
func delete(m map[Type]Type1, key Type) 复制代码
第一个参数是 Map
,第二个参数是 key
。
m := map[string]int{ "January":1, "February":2, "March":3, } fmt.Println(m) // 输出:map[March:3 January:1 February:2] delete(m,"January") fmt.Println(m) // 输出:map[February:2 March:3] 复制代码
删除一个不存在的键值对时, delete
函数不会报错,没任何作用。
遍历 Map
Map
没法使用 for
循环遍历,跟数组、切片一样,可以使用 range
遍历。
for key, value := range m { fmt.Println(key, "=>", value) } 复制代码
输出 :
February => 2 March => 3 January => 1 复制代码
可以使用空白操作符 _
忽略返回的 key
或 value
。多次执行代码的时候,你会发现,返回值的顺序有可能是不一样的,也就是说 Map
的遍历是无序的。
len
函数
可以使用 len
函数返回Map中键值对的数量:
fmt.Println("len(m) =",len(m)) 复制代码
Map
是一种引用类型
Map
是对底层数据的引用 。编写代码的过程中,会涉及到 Map
拷贝、函数间传递 Map
等。跟 Slice
类似, Map
指向的底层数据是不会发生 copy
的。
m := map[string]int{ "January":1, "February":2, "March":3, } month := m delete(month,"February") fmt.Println(m) fmt.Println(month) 复制代码
输出 :
map[January:1 March:3] map[January:1 March:3] 复制代码
上面的代码,将 Map
m
赋值给 month
,删除了 month
中的一个键值对, m
也发生了改变,说明 Map
拷贝时, m
和 month
是共享底层数据的,改变其中一方数据,另一方也会随之改变。类似,在函数间传递 Map
时,其实传递的是 Map
的引用,不会涉及底层数据的拷贝,如果在被调用函数中修改了 Map
,在调用函数中也会感知到 Map
的变化。 那如果我真想拷贝一个 Map
怎么办?
month := map[string]int{} m := map[string]int{ "January":1, "February":2, "March":3, } for key,value := range m{ month[key] = value } delete(month,"February") fmt.Println(m) fmt.Println(month) 复制代码
输出 :
map[January:1 February:2 March:3] map[January:1 March:3] 复制代码
上面的代码,我们使用 range
将 m
的键值对循环赋值给了 month
,然后删除 month
其中一个键值对,通过打印的结果可以看出, m
没有改变。这就实现了真正的拷贝。 关于 Map
的使用就讲到这,欢迎大家评论交流!
关注公众号「Golang来了」,获取最新文章!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。