内容简介:我们来试着创建:
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来了」,获取最新文章!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。