0
点赞
收藏
分享

微信扫一扫

Go语言实战之映射的内部实现和基础功能


Go语言实战之映射的内部实现和基础功能_golang

写在前面

  • 嗯,学习​​GO​​,所以有了这篇文章
  • 博文内容为​​《GO语言实战》​​读书笔记之一
  • 主要涉及映射相关知识

你要爱就要像一个痴情的恋人那样去爱,像一个忘死的梦者那样去爱,视他人之疑目如盏盏鬼火,大胆去走你的夜路。——史铁生《病隙碎笔》

映射的内部实现和基础功能

​映射​​​是一种​​数据结构​​​,是用于存储一系列​​无序​​​的​​键值对​​​。类比​​Java里的Map​​​,​​Python里的字典​​​,可以理解为以​​哈希值​​​做​​索引​​​,​​期望索引​​​可以在一定的连续内存范围内的​​类似数组的数据结构​​。

​映射​​​里基于​​键来存储值​​​。映射功能强大的地方是,能够基于​​键快速检索数据​​。键就像索引一样,指向与该键关联的值。

内部实现

映射是一个​​集合​​​,可以使用类似处理数组和切片的方式​​迭代​​​映射中的元素。但映射是​​无序的集合​​​,无序的原因是映射的实现使用了​​散列表​​.

映射的​​散列表​​​包含一组​​桶​​。

在存储、删除或者查找键值对的时候,所有操作都要先选择一个​​桶​​​。把操作映射时指定的​​键​​​传给映射的​​散列函数​​​,就能选中对应的​​桶​​。

这个​​散列函数​​​的目的是生成一个​​索引​​​,这个​​索引​​​最终将​​键值对​​​分布到所有可用的​​桶​​​里。对 Go 语言的映射来说,生成的散列键的一部分,具体来说是​​低位(LOB)​​​,被用来​​选择桶​​。

Go语言实战之映射的内部实现和基础功能_golang_02

桶的内部实现。​​映射​​使用两个数据结构来存储数据,

  • 第一个是​​数组​​​,内部存储用于​​选择桶​​​的​​散列键​​​的​​高八位值​​。用于区分每个键值对要存在桶里的那一项。
  • 第二个是​​字节数组​​​,用于存储​​键值对​​​。该字节数组先​​依次存储​​​了这个桶里​​所有的键​​​,之后​​依次存储​​​了这个桶里​​所有的值​​。实现这种键值对的存储方式目的在于减少每个桶所需的内存。

映射存储的增加,索引分布越均匀,访问键值对的速度就越快,随着映射存储的增加,​​索引分布越均匀,访问键值对的速度就越快​​​。​​映射通过合理数量的桶来平衡键值对的分布​

创建和初始化

Go 语言中有很多种方法可以创建并初始化映射,可以使用内置的​​make 函数​​​,也可以使用​​映射字面量​​。

package main

import (
"fmt"
)

func main() {
// 创建一个映射,键的类型是 string,值的类型是 int
dict := make(map[string]int)
// 创建一个映射,键和值的类型都是 string
// 使用两个键值对初始化映射
dict_ := map[string]string{"Red": "#da1337", "Orange": "#e95a22"}
fmt.Println(dict)
fmt.Print(dict_)
}

======
map[]
map[Orange:#e95a22 Red:#da1337]

创建映射时,更常用的方法是使用​​映射字面量​​。映射的初始长度会根据初始化时指定的键值对的数量来确定。

映射的键可以是​​任何值​​​。这个值的类型可以是​​内置的类型​​​,也可以是​​结构类型​​​,只要这个值可以使用​​==​​运算符做比较

​切片、函数以及包含切片​​​的​​结构类型​​​由于具有引用语义,不能作为​​映射的键​​,使用这些类型会造成编译错误

package main

import (
"fmt"
)

func main() {
// 创建一个映射,使用字符串切片作为映射的键
dict := map[[]string]int{}
fmt.Println(dict)
}
====
[Running] go run "d:\GolandProjects\code-master\demo\hello.go"
# command-line-arguments
demo\hello.go:10:45: duplicate key "Red" in map literal
previous key at demo\hello.go:10:28

[Done] exited with code=2 in 0.902 seconds

  • 声明一个存储字符串切片的映射

// 创建一个映射,使用字符串切片作为值
dict := map[int][]string{}

使用映射

键值对赋值给映射,是通过指定适当类型的键并给这个键赋一个值来完成的

  • 为映射赋值

// 创建一个空映射,用来存储颜色以及颜色对应的十六进制代码
colors := map[string]string{}
// 将 Red 的代码加入到映射
colors["Red"] = "#da1337"

  • 可以通过声明一个未初始化的映射来创建一个值为​​nil​​的映射,不能用于存储键值对.

// 通过声明映射创建一个 nil 映射
var colors map[string]string
// 将 Red 的代码加入到映射
colors["Red"] = "#da1337"
======
Runtime Error:
panic: runtime error: assignment to entry in nil map

从​​映射取值​​时有两个选择:

第一个选择是,可以同时获得值,以及一个表示这个键是否存在的标志,

  • 从映射获取值并判断键是否存在

// 获取键 Blue 对应的值
value, exists := colors["Blue"]
// 这个键存在吗?
if exists {
fmt.Println(value)
}

另一个选择是,只返回键对应的值,然后通过判断这个值是不是零值来确定键是否存在

  • 从映射获取值,并通过该值判断键是否存在

// 获取键 Blue 对应的值
value := colors["Blue"]
// 这个键存在吗?
if value != "" {
fmt.Println(value)
}

在​​Go​​​语言里,通过键来索引映射时,即便这个​​键不存在也总会返回一个值​​​。在这种情况下,返回的是该值对应的类型的​​零值​

​迭代映射​​里的所有值和迭代数组或切片一样,使用关键字 range

  • 使用​​range​​ 迭代映射

// 创建一个映射,存储颜色以及颜色对应的十六进制代码
colors := map[string]string{
"AliceBlue": "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}
// 显示映射里的所有颜色
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}

想把一个键值对从映射里删除,就使用内置的​​delete 函数​

  • 从映射中删除一项

// 删除键为 Coral 的键值对
delete(colors, "Coral")
// 显示映射里的所有颜色
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}

在函数间传递映射

在函数间传递映射​​并不会制造出该映射的一个副本​​​。实际上,当传递映射给一个函数,并对这个映射做了修改时,所有对这个映射的引用都会​​察觉​​到这个修改,这个特性和切片类似,保证可以用很小的成本来复制映射

package main

import (
"fmt"
)

func main() {
// 创建一个映射,存储颜色以及颜色对应的十六进制代码
colors := map[string]string{
"AliceBlue": "#f0f8ff",
"Coral": "#ff7F50",
"DarkGray": "#a9a9a9",
"ForestGreen": "#228b22",
}
// 显示映射里的所有颜色
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
fmt.Println("调用函数来移除指定的键")
// 调用函数来移除指定的键
removeColor(colors, "Coral")

// 显示映射里的所有颜色
for key, value := range colors {
fmt.Printf("Key: %s Value: %s\n", key, value)
}
}

// removeColor 将指定映射里的键删除
func removeColor(colors map[string]string, key string) {
delete(colors, key)
}

[Running] go run "d:\GolandProjects\code-master\demo\hello.go"
Key: Coral Value: #ff7F50
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22
Key: AliceBlue Value: #f0f8ff
调用函数来移除指定的键
Key: AliceBlue Value: #f0f8ff
Key: DarkGray Value: #a9a9a9
Key: ForestGreen Value: #228b22

[Done] exited with code=0 in 1.419 seconds

映射的增长没有容量或者任何限制。同时​​内置函数 len​​​可以用来获取​​切片​​​或者​​映射​​​的​​长度​​​。但是​​内置函数 cap​​​只能用于​​切片​​。

Go语言实战之映射的内部实现和基础功能_开发语言_03


举报

相关推荐

0 条评论