[奔跑的 Go] 教程十、 Go 语言 Map
Map(映射)是一种复合数据类型,它通过键:值对的形式来表示数据。
Map(映射)就像一个有许多房间的公寓大楼。每个房间都有钥匙,进入房间里面找东西,必须需要钥匙来开门才能进入。钥匙可以由任何固定材料(数据类型)组成,房间里面可以含有任何的固定材料(数据类型)。
☛ 什么是映射?
一个映射就像一个array
,除了整数index
之外,你可以将string
或任何其他数据类型作为key
。映射的例子看起来如下:
{
stringKey: intValue,
stringKey: intValue
...
}
定义一个映射的语法格式如下:
var myMap map[keyType]valueType
上面keyType
是这个映射所有键的数据类型,valueType
是这个映射所有值的数据类型。map
是一种复合数据类型因为它是由基础数据类型组成的 (查阅变量课程 基础数据类型)。
让我们来定义一个简单的映射。
https://play.golang.org/p/Rv6dEDtbsoZ
在上面的程序中,我们声明了一个空的映射 m
,因为 zero-value
的映射是 nil
。 但是对于 nil
映射而言, 我们不能为它添加任何值,因为它和切片一样,map
不能保存任何数据,而是引用了保存数据的内部数据结构。因此,在 nil
映射的情况下,缺少内部数据结构并为其分配任何数据时,将导致运行时出现panic, panic: assignment to entry in nil map
。你可以使用 nil
映射作为一个变量去存储另一个非 nil
映射。
☛ 创建空映射
一个empty map
(空映射)就像一个空的slice
,内部数据结构被定义,以便我们可以用它来存储一些数据。像slice
一样,我们可以使用make
函数来创建一个空的map
。
m := make(map[keyType]valueType)
让我们创建一个简单的映射age
,我们可以用来保存小伙伴们的年龄。
https://play.golang.org/p/mrQ_Hw0lZI0
在上面的程序中,我们创建了空映射age
,它包含int
数据并由string
键引用。你可以使用“map [key]”之类的键来访问或指定映射的值。
利用这些信息,我们分配了一些“mina”,“john”和“mike”的年龄数据。你可以根据需要添加任意数量的值,因为像slice
一样map
可以包含可变数量的元素。
☛ 映射初始化
与array
和slice
类似,我们可以在创建 映射时初始化数据,而不是先创建一个empty
映射再分配新数据。
https://play.golang.org/p/49br11mmkqx
☛ 访问映射数据
在使用 数组(array)
或者 切片(slice)
时,当我们访问一个超出界限的索引(不存在的索引)时,go 会抛出一个错误,但映射(map)并非如此。当你通过不存在于映射(map)中的 键(key)
来访问数据时, go 并不会跑出错误,而是返回 元素类型
对应数据类型的 零值
。
https://play.golang.org/p/fq6i39u8AeF
返回值 28
是元素 mina
的年龄,但是当访问元素 jessy
的年龄是,go 返回的结果是 0
。这是因为 jessy
并不在映射(map)中,而年龄(age)字段的类型是 int
,故返回的是 0
。
所以为了能够判断一个键(key)是否存在映射(map)中,go 提供了另外一个具有 2 个返回值的语法。
value, ok := m[key]
如你所见,上面的示例正式这种用法。
https://play.golang.org/p/YYYT2qdiGue
所以,我们可以通过这个额外的返回参数来判断键(key)是否存在于映射(map)中。如果存在,第二个参数值为 true
否则为 false
。
☛ 映射的长度
使用 len
可以查看一个映射中包含多少个元素,就是那些可以通过 array
和 slice
来查看的元素。
https://play.golang.org/p/94LOJeBXfq7
在映射中,没有类似容量的概念,这是由于 go 完全控制着映射内部的数据接口。因此,不要尝试对一个映射使用
cap
。
☛ Delete map
element
和 slice
不同,你需要使用 hack 来删除它的元素,对于映射,go提供了一个更简单的函数 delete
来删除 map
的元素。一个删除函数的语法如下
func delete(m map[Type]Type1, key Type)
delete
函数要求第一个参数是一个 map
,第二个参数是 key
值。
https://play.golang.org/p/i_M0xZ6jleP
如果键不在映射中,就像上面例子中的
jessy
,go在执行delete
函数时不会抛出错误。
☛ 映射的比较
同 slice(切片)
一样,映射只能与nil
进行比较。比较二个映射时你想遍历映射并匹配每个映射中的元素,这将是件麻烦事。如果你确实需要比较二个映射,使用reflect
包中的DeepEqual
函数 (https://golang.org/pkg/reflect/)。
☛ 映射的遍历
由于map
中没有索引值,所以不能使用简单的for
循环来增加index
值,直到它到达结尾。你需要使用for range
来遍历映射。
https://play.golang.org/p/MbLYk17Zu6C
for
循环中的range
将返回映射元素的key
和value
。当你不需要key
或value
时你也可以使用_
(空白标识符)来忽略它,就像array(数组)
和slice(切片)
一样。
当使用
for range
来遍历映射时,映射检索出来的元素顺序是随机的。因此,无法保证每次都能按顺序排列。这就解释了为什么我们无法比较二个映射。☛ 映射与其它数据类型
并不是只能使用字符串类型来表示一个映射的键,任何可比较的类型,如布尔值,整型,浮点数,复数,字符串等也可以是键。很显然(布尔类型)boolean
只能表示二个值,因为它只能是true
或者false
。让我们看看使用它时会发生什么。
https://play.golang.org/p/NbeIj54VdFS
我们使用(布尔类型)boolean
作为键值来实现了一个示例。但当我们添加重复键的时候会发生什么。
https://play.golang.org/p/sOC6TpettrK
这表明,我们不能在映射中添加重复的键。
☛ 映射是引用数据类型
如同 (切片)slice
,映射是内部的数据结构的引用。当你复制一个映射到一个新映射时,它内部的数据结构不会被复制,只是被新映射引用。
https://play.golang.org/p/n-U1AF4jy8M
正如所料,因为我们删除了一个元素,所以这个名为ages
的映射现在拥有二个元素。不仅如此,名为age
的映射也有相同的变化。显然,不要 滥用映射。
要复制一个映射,你需要使用 for
循环。
https://play.golang.org/p/uqwruAyVym4
在上面的例子中,我们没有复制映射而是使用映射的 keys 和 values 保存在不同的映射中,这些映射实现了它自己的底层数据结构。
由于
map
引用了内部数据结构,所以作为函数的参数传递时,map
与slice
共享相同的内部数据结构。因此,请确保遵循slices
课程中所述相同指导原则。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: