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