Golang 语言基础之四: map, range

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

Golang 语言基础之四: map, range

Golang 语言基础系列:

Map

map 是 Golang 中的一种 Associative data type。提供类似于其他语言中 hash 或者 dictionary 的功能。map 类型是 Golang 语言中 3 个引用类型(slice, map, channel)之一。map 对象中的 Keys 元素必须不重复而且属于同一类型,该类型必须支持 == 操作符,可以用于比较;Values 元素也必须属于同一类型,该类型可以是任意类型。

map 对象的声明形式为 map[TypeOfKey]TypeOfValue

还是从例子入手:

package main

import "fmt"

func main() {
	// 如果已经知道 map 中的数据,可以直接以下面的方式声明 map 对象
	v_map := map[int]string{
		1: "One",
		2: "Two",
		3: "Three",
	}
	fmt.Println("v_map: ", v_map)
	fmt.Println("len(v_map): ", len(v_map))

	// 从下面的用法可以看到 “修改已有值” 和 “添加新 pair” 形式上是一样的。
	// 如果 key 值已存在,就是修改操作,否则就是添加新 pair
	// 修改已有值
	v_map[1] = "One hundred"

	// 添加新 pair
	v_map[4] = "Two hundred"
	fmt.Println("After changes, v_map: ", v_map)
	fmt.Println("After changes, len(v_map): ", len(v_map))

	// 如果只是声明一个 map 对象,可以使用内置函数 make
	v_mapByMake := make(map[int]string)
	v_mapByMake[0] = "One hundred"
	fmt.Println("make(map[int]string, 10),v_mapByMake: ", v_mapByMake)

	// 使用 make 时也可以设定预期的键值对数量,在初始化时一次性分配大量内存,从而避免使用过程中频繁动态分配
	// 这里给定的数量值不会影响初始化后 len(mapObject)
	v_mapByMake = make(map[int]string, 10)
	fmt.Println("make(map[int]string, 10),Before chagnes,v_mapByMake: ", v_mapByMake)
	fmt.Println("make(map[int]string, 10),Before chagnes,len(v_mapByMake): ", len(v_mapByMake))
	v_mapByMake[0] = "One hundred"
	v_mapByMake[1] = "Two hundred"
	v_mapByMake[2] = "Three hundred"
	fmt.Println("make(map[int]string, 10),After chagnes,v_mapByMake: ", v_mapByMake)

	// 遍历 map 对象中的键值对
	// 注意,这里迭代的顺序是不确定的
	for key, value := range v_mapByMake {
		fmt.Printf("%d : %s\n", key, value)
	}

	// 检查 map 对象中是否存在某个 key 索引的元素,如果存在获取该 key 索引的 value
	if value, ok := v_mapByMake[1]; ok {
		fmt.Println("value, ok := v_mapByMake[1],value: ", value)
	}

	// 这里如果只是判断是否存在,可以使用占位符
	_, ok := v_mapByMake[1]
	fmt.Println("_, ok := v_mapByMake[1],ok: ", ok)

	// 如果尝试获取不存在的元素,会返回空,不会抛出异常
	fmt.Println("v_mapByMake[10]: ", v_mapByMake[10])

	// 如果尝试删除不存在的元素,对已有数据不会有影响,不会抛出异常
	fmt.Println("Before deletion non-existed elem, v_mapByMake: ", v_mapByMake)
	delete(v_mapByMake, 10)
	fmt.Println("After deletion non-existed elem, v_mapByMake: ", v_mapByMake)

	// 从 map 中获取的 value 是原始数据的拷贝,如果其本身是值类型,对其修改时不允许的。
	// 如果是引用类型,修改就没有问题。
	// 比如下面的例子,如果值是 slice 对象,对其元素的修改是允许的。
	// 如果值是 array 对象,则通过 map 的索引获取到的 value 是不允许修改的。
	// 将下面 map 的元素类型修改为 array 类型 map[int][3]int, 尝试修改会导致编译错误
	v_mapOfArray := map[int][]int{
		1: {0, 1, 2},
		2: {3, 4, 5},
	}
	fmt.Println("Before change, v_mapOfArray is: ", v_mapOfArray)
	fmt.Println("Before change, v_mapOfArray[1][0] is: ", v_mapOfArray[1][0])
	v_mapOfArray[1][0] = 100
	fmt.Println("After change, v_mapOfArray[1][0] is: ", v_mapOfArray[1][0])
	fmt.Println("After change, v_mapOfArray is: ", v_mapOfArray)

	v_map = map[int]string{
		1: "One",
		2: "Two",
		3: "Three",
	}

	fmt.Println("Before iterating, v_map: ", v_map)
	// 迭代过程中可以安全地删除键值对,在迭代过程中也支持添加新的键值对
	for key, _ := range v_map {
		delete(v_map, key)
		if key == 1 {
			v_map[key*5] = "New"
		}
	}
	fmt.Println("After iterating, v_map: ", v_map)

}

将上面的代码存入源文件 map.go 并使用 go run map.go 可以看到下面的输入:

v_map:  map[1:One 2:Two 3:Three]
len(v_map):  3
After changes, v_map:  map[1:One hundred 2:Two 3:Three 4:Two hundred]
After changes, len(v_map):  4
make(map[int]string, 10),v_mapByMake:  map[0:One hundred]
make(map[int]string, 10),Before chagnes,v_mapByMake:  map[]
make(map[int]string, 10),Before chagnes,len(v_mapByMake):  0
make(map[int]string, 10),After chagnes,v_mapByMake:  map[0:One hundred 1:Two hundred 2:Three hundred]
2 : Three hundred
0 : One hundred
1 : Two hundred
value, ok := v_mapByMake[1],value:  Two hundred
_, ok := v_mapByMake[1],ok:  true
v_mapByMake[10]:  
Before deletion non-existed elem, v_mapByMake:  map[2:Three hundred 0:One hundred 1:Two hundred]
After deletion non-existed elem, v_mapByMake:  map[2:Three hundred 0:One hundred 1:Two hundred]
Before change, v_mapOfArray is:  map[1:[0 1 2] 2:[3 4 5]]
Before change, v_mapOfArray[1][0] is:  0
After change, v_mapOfArray[1][0] is:  100
After change, v_mapOfArray is:  map[1:[100 1 2] 2:[3 4 5]]
Before iterating, v_map:  map[1:One 2:Two 3:Three]
After iterating, v_map:  map[5:New]

注意,官方文档 中对使用内置函数 make 创建 map 对象时这样说:

make(T)          map        map of type T
make(T, n)       map        map of type T with initial space for n elements

从中可以看到,我们可以在创建 map 对象时设定预期的元素个数,这样在 map 对象中的键值对增加时避免频繁动态分配内存,提高效率。

Range

官网 For statement 对 range 表达式的返回值做了如下说明:

Range expression                          1st value          2nd value

array or slice  a  [n]E, *[n]E, or []E    index    i  int    a[i]       E
string          s  string type            index    i  int    see below  rune
map             m  map[K]V                key      k  K      m[k]       V
channel         c  chan E, <-chan E       element  e  E
  1. 如果 a 是一个数组、数组的指针或者切片(slice),产生的索引以递增的顺序创建,如果索引是 for 语句的参数,则其从 0 开始到 len(a) - 1。对于空 slice 来说,索引总数为零。
  2. 对于 string 类型的 arange 从该字符串中首个字符开始,每次增加一个 Unicode code points。在连续的迭代过程中,index 的索引为字符串经过 UTF-8 编码后连续的 code points 的首个字节索引,rune 类型的 range 返回的第二个值就是当前 UTF-8 code point的值。如果迭代过程遇到非法的 UTF-8 编码,则 range 返回的第二个值规定为 0xFFFD,一个 Unicode 替代字符,然后迭代器向前前进一个字节。
  3. map 中的索引顺序没有定义(not specified),Golang 不保证使用不同的索引后结果的顺序相同。如果 map 中的 pair 还未被访问时被删除了,该 pair 对应的 iteration 就不会生成。如果在迭代过程中创建新的键值对,可以选择是否在哪个具体的迭代过程中来创建。如果 map 对象是空的,则索引总数为零。
  4. 对于 channel 对象来说,range 产生的索引的值来自这个发送到该 channel 对象的连续的值,直到该对象被关闭(closed)。如果对象为 nilrange 表达式会被一直 block。

参考资料

-- EOF --

声明: 本文采用 BY-NC-SA 协议进行授权. 转载请注明转自: Golang 语言基础之四: map, range


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

本文来自:Ross's Page

感谢作者:Xiaohui Wang

查看原文:Golang 语言基础之四: map, range

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

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