《Go语言四十二章经》第十三章 字典(Map)

roteman · · 979 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

《Go语言四十二章经》第十三章 字典(Map) 作者:李骁 ## 13.1 字典(Map) **map 是引用类型**,可以使用如下声明: ```go var map1 map[keytype]valuetype var map1 map[string]int ``` ([keytype] 和 valuetype 之间允许有空格,但是 Gofmt 移除了空格) 在声明的时候不需要知道 map 的长度,map 是可以动态增长的。 未初始化的 map 的值是 nil。 key 可以是任意可以用 == 或者 != 操作符比较的类型,比如 string、int、float。所以数组、切片和结构体不能作为 key (译者注:含有数组切片的结构体不能作为 key,只包含内建类型的 struct 是可以作为 key 的),但是指针和接口类型可以。如果要用结构体作为 key 可以提供 Key() 和 Hash() 方法,这样可以通过结构体的域计算出唯一的数字或者字符串的 key。 value 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言。map 也可以用函数作为自己的值,这样就可以用来做分支结构:key 用来选择要执行的函数。 map 传递给函数的代价很小:在 32 位机器上占 4 个字节,64 位机器上占 8 个字节,无论实际上存储了多少数据。 **通过 key 在 map 中寻找值是很快的,比线性查找快得多,但是仍然比从数组和切片的索引中直接读取要慢 100 倍;所以如果你很在乎性能的话还是建议用切片来解决问题。** map 可以用 {key1: val1, key2: val2} 的描述方法来初始化,就像数组和结构体一样。 map 是 引用类型 的: 内存用 make 方法来分配。 map 的初始化: ```go var map1 = make(map[keytype]valuetype) ``` map 容量: 和数组不同,map 可以根据新增的 key-value 对动态的伸缩,因此它不存在固定长度或者最大限制。但是你也可以选择标明 map 的初始容量 capacity,就像这样:make(map[keytype]valuetype,cap)。 例如: ```go map2 := make(map[string]float32, 100) ``` 当 map 增长到容量上限的时候,如果再增加新的 key-value 对,map 的大小会自动加 1。所以出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,也最好先标明。 在一个 nil 的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic。 ```go Works: package main func main() { var s []int s = append(s, 1) } Fails: package main func main() { var m map[string]int m["one"] = 1 // 错误 } ``` map的key访问,val1, isPresent := map1[key1] 或者 val1 = map1[key1] 的方法获取 key1 对应的值 val1。 一般判断是否某个key存在,不使用值判断,而使用下面的方式: ```go if _, ok := x["two"]; !ok { fmt.Println("no entry") } ``` **用切片作为 map 的值** 既然一个 key 只能对应一个 value,而 value 又是一个原始类型,那么如果一个 key 要对应多个值怎么办?例如,当我们要处理unix机器上的所有进程,以父进程(pid 为整形)作为 key,所有的子进程(以所有子进程的 pid 组成的切片)作为 value。通过将 value 定义为 []int 类型或者其他类型的切片,就可以优雅的解决这个问题。 这里有一些定义 map 的例子: ```go // 声明但未初始化map,此时是map的零值状态 map1 := make(map[string]string, 5) map2 := make(map[string]string) // 创建了初始化了一个空的的map,这个时候没有任何元素 map3 := map[string]string{} // map中有三个值 map4 := map[string]string{"a": "1", "b": "2", "c": "3"} ``` 从 map1 中删除 key1: 直接 delete(map1, key1) 就可以。如果 key1 不存在,该操作不会产生错误。 ```go Delete(map4, "a") ``` map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。 如果你想为 map 排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort 包)。 ## 13.2 "range"语句中更新引用元素的值 在"range"语句中生成的数据的值是真实集合元素的拷贝。它们不是原有元素的引用。 这意味着更新这些值将不会修改原来的数据。同时也意味着使用这些值的地址将不会得到原有数据的指针。 ```go package main import "fmt" func main() { data := []int{1, 2, 3} for _, v := range data { v *= 10 // 通常数据项不会改变 } fmt.Println("data:", data) // 程序输出: [1 2 3] } ``` 如果你需要更新原有集合中的数据,使用索引操作符来获得数据。 ```go package main import "fmt" func main() { data := []int{1, 2, 3} for i, _ := range data { data[i] *= 10 } fmt.Println("data:", data) // 程序输出 data: [10 20 30] } ``` >本书《Go语言四十二章经》内容在github上同步地址:https://github.com/ffhelicopter/Go42 >本书《Go语言四十二章经》内容在简书同步地址: https://www.jianshu.com/nb/29056963 > >虽然本书中例子都经过实际运行,但难免出现错误和不足之处,烦请您指出;如有建议也欢迎交流。 >联系邮箱:roteman@163.com

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

979 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传