《快学 Go 语言》第 6 课 —— 字典

简介:

字典在数学上的词汇是映射,将一个集合中的所有元素关联到另一个集合中的部分或全部元素,并且只能是一一映射或者多对一映射。

9d7f4fef7de971f6dbec0e601c10679804451e39

数组切片让我们具备了可以操作一块连续内存的能力,它是对同质元素的统一管理。而字典则赋予了不连续不同类的内存变量的关联性,它表达的是一种因果关系,字典的 key 是因,字典的 value 是果。如果说数组和切片赋予了我们步行的能力,那么字典则让我们具备了跳跃的能力。

指针、数组切片和字典都是容器型变量,字典比数组切片在使用上要简单很多,但是内部结构却无比复杂。本节我们只专注字典的基础使用,在后续的高级章节再来分析它的内部结构。

字典的创建

关于 Go 语言有很多批评的声音,比如说它不支持范型。其实严格来说 Go 是支持范型的,只不过很弱,范型在 Go 语言里是一种很弱的存在。比如数组切片和字典类型都是支持范型的。在创建字典时,必须要给 key 和 value 指定类型。创建字典也可以使用 make 函数

package main

import "fmt"

func main() {
var m map[int]string = make(map[int]string)
fmt.Println(m, len(m))
}

----------
map[] 0


使用 make 函数创建的字典是空的,长度为零,内部没有任何元素。如果需要给字典提供初始化的元素,就需要使用另一种创建字典的方式。

package main

import "fmt"

func main() {
var m map[int]string = map[int]string{
90: "优秀",
80: "良好",
60: "及格", // 注意这里逗号不可缺少,否则会报语法错误
}
fmt.Println(m, len(m))
}

---------------
map[90:优秀 80:良好 60:及格] 3


字典变量同样支持类型推导,上面的变量定义可以简写成

var m = map[int]string{
90: "优秀",
80: "良好",
60: "及格",
}


如果你可以预知字典内部键值对的数量,那么还可以给 make 函数传递一个整数值,通知运行时提前分配好相应的内存。这样可以避免字典在长大的过程中要经历的多次扩容操作。

var m = make(map[int]string, 16)

同 Python 语言一样,字典可以使用中括号来读写内部元素,使用 delete 函数来删除元素。

package main

import "fmt"

func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}
// 读取元素
var score = fruits["banana"]
fmt.Println(score)

// 增加或修改元素
fruits["pear"] = 3
fmt.Println(fruits)

// 删除元素
delete(fruits, "pear")
fmt.Println(fruits)
}

-----------------------
5
map[apple:2 banana:5 orange:8 pear:3]
map[orange:8 apple:2 banana:5]

删除操作时,如果对应的 key 不存在,delete 函数会静默处理。遗憾的是 delete 函数没有返回值,你无法直接得到 delete 操作是否真的删除了某个元素。你需要通过长度信息或者提前尝试读取 key 对应的 value 来得知。

读操作时,如果 key 不存在,也不会抛出异常。它会返回 value 类型对应的零值。如果是字符串,对应的零值是空串,如果是整数,对应的零值是 0,如果是布尔型,对应的零值是 false。

你不能通过返回的结果是否是零值来判断对应的 key 是否存在,因为 key 对应的 value 值可能恰好就是零值,比如下面的字典你就不能判断 "durin" 是否存在

var m = map[string]int {
"durin": 0 // 举个栗子而已,其实我还是喜欢吃榴莲的
}


这时候必须使用字典的特殊语法,如下

package main

import "fmt"

func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}

var score, ok = fruits["durin"]
if ok {
fmt.Println(score)
} else {
fmt.Println("durin not exists")
}

fruits["durin"] = 0
score, ok = fruits["durin"]
if ok {
fmt.Println(score)
} else {
fmt.Println("durin still not exists")
}
}

-------------
durin not exists
0


字典的下标读取可以返回两个值,使用第二个返回值都表示对应的 key 是否存在。初学者看到这种奇怪的用法是需要花时间来消化的,读者不需要想太多,它只是 Go 语言提供的语法糖,内部并没有太多的玄妙。正常的函数调用可以返回多个值,但是并不具备这种“随机应变”的特殊能力 —— 「多态返回值」。字典的遍历

字典的遍历提供了下面两种方式,一种是需要携带 value,另一种是只需要 key,需要使用到 Go 语言的 range 关键字。

package main

import "fmt"

func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}

for name, score := range fruits {
fmt.Println(name, score)
}

for name := range fruits {
fmt.Println(name)
}
}

------------
orange 8
apple 2
banana 5
apple
banana
orange


奇怪的是,Go 语言的字典没有提供诸于 keys() 和 values() 这样的方法,意味着如果你要获取 key 列表,就得自己循环一下,如下

package main

import "fmt"

func main() {
var fruits = map[string]int {
"apple": 2,
"banana": 5,
"orange": 8,
}

var names = make([]string, 0, len(fruits))
var scores = make([]int, 0, len(fruits))

for name, score := range fruits {
names = append(names, name)
scores = append(scores, score)
}

fmt.Println(names, scores)
}

----------
[apple banana orange] [2 5 8]


这会让代码写起来比较繁琐,不过 Go 语言官方就是没有提供,读者还是努力习惯一下吧

线程(协程)安全

Go 语言的内置字典不是线程安全的,如果需要线程安全,必须使用锁来控制。在后续锁的章节里,我们将会自己实现一个线程安全的字典。

字典变量里存的是什么?

字典变量里存的只是一个地址指针,这个指针指向字典的头部对象。所以字典变量占用的空间是一个字,也就是一个指针的大小,64 位机器是 8 字节,32 位机器是 4 字节。

23a45a10e3d606b3678e0a2b9840f27d34ef1804图片


可以使用 unsafe 包提供的 Sizeof 函数来计算一个变量的大小

package main

import (
"fmt"
"unsafe"
)

func main() {
var m = map[string]int{
"apple": 2,
"pear": 3,
"banana": 5,
}
fmt.Println(unsafe.Sizeof(m))
}

------
8

在遍历字典得到 keys 和 values 的例子里,我们分配了 names 和 scores 两个切片,如果把代码片断调整成下面这样,会有什么问题?

var names = make([]string, len(fruits))
var scores = make([]int, len(fruits))


下一节我们开讲 Go 语言的字符串


原文发布时间为: 2018-11-16
本文作者:码洞
本文来自云栖社区合作伙伴“码洞”,了解相关信息可以关注“码洞”。

相关文章
|
3天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
51 20
|
1天前
|
存储 监控 算法
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
9天前
|
Go C语言
Go语言入门:分支结构
本文介绍了Go语言中的条件语句,包括`if...else`、`if...else if`和`switch`结构,并通过多个练习详细解释了它们的用法。`if...else`用于简单的条件判断;`if...else if`处理多条件分支;`switch`则适用于基于不同值的选择逻辑。特别地,文章还介绍了`fallthrough`关键字,用于优化重复代码。通过实例如判断年龄、奇偶数、公交乘车及成绩等级等,帮助读者更好地理解和应用这些结构。
34 14
|
23天前
|
存储 监控 算法
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
27 5
|
1月前
|
算法 安全 Go
Go语言中的加密和解密是如何实现的?
Go语言通过标准库中的`crypto`包提供丰富的加密和解密功能,包括对称加密(如AES)、非对称加密(如RSA、ECDSA)及散列函数(如SHA256)。`encoding/base64`包则用于Base64编码与解码。开发者可根据需求选择合适的算法和密钥,使用这些包进行加密操作。示例代码展示了如何使用`crypto/aes`包实现对称加密。加密和解密操作涉及敏感数据处理,需格外注意安全性。
45 14
|
1月前
|
Go 数据库
Go语言中的包(package)是如何组织的?
在Go语言中,包是代码组织和管理的基本单元,用于集合相关函数、类型和变量,便于复用和维护。包通过目录结构、文件命名、初始化函数(`init`)及导出规则来管理命名空间和依赖关系。合理的包组织能提高代码的可读性、可维护性和可复用性,减少耦合度。例如,`stringutils`包提供字符串处理函数,主程序导入使用这些函数,使代码结构清晰易懂。
94 11
|
1月前
|
存储 安全 Go
Go语言中的map数据结构是如何实现的?
Go 语言中的 `map` 是基于哈希表实现的键值对数据结构,支持快速查找、插入和删除操作。其原理涉及哈希函数、桶(Bucket)、动态扩容和哈希冲突处理等关键机制,平均时间复杂度为 O(1)。为了确保线程安全,Go 提供了 `sync.Map` 类型,通过分段锁实现并发访问的安全性。示例代码展示了如何使用自定义结构体和切片模拟 `map` 功能,以及如何使用 `sync.Map` 进行线程安全的操作。
|
8天前
|
监控 关系型数据库 MySQL
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
【01】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-硬件设备实时监控系统运营版发布-本产品基于企业级开源项目Zabbix深度二开-分步骤实现预计10篇合集-自营版
18 0
|
1月前
|
监控 安全 算法
深度剖析核心科技:Go 语言赋能局域网管理监控软件进阶之旅
在局域网管理监控中,跳表作为一种高效的数据结构,能显著提升流量索引和查询效率。基于Go语言的跳表实现,通过随机化索引层生成、插入和搜索功能,在高并发场景下展现卓越性能。跳表将查询时间复杂度优化至O(log n),助力实时监控异常流量,保障网络安全与稳定。示例代码展示了其在实际应用中的精妙之处。
44 9
|
2月前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
64 12

热门文章

最新文章