[奔跑的 Go] 教程十、 Go 语言 Map

Golang

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可以包含可变数量的元素。

☛ 映射初始化

arrayslice类似,我们可以在创建 映射时初始化数据,而不是先创建一个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 可以查看一个映射中包含多少个元素,就是那些可以通过 arrayslice 来查看的元素。

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将返回映射元素的keyvalue。当你不需要keyvalue时你也可以使用_(空白标识符)来忽略它,就像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 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://medium.com/rungo/the-anatomy-of-...

译文地址:https://learnku.com/go/t/28989

本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!