map 遍历
遍历的顺序是随机的
基本的遍历方式
func main() {
m := map[string]int{
"a": 1,
"b": 2,
}
for k, v := range m {
fmt.Println(k, v)
}
}
/* 可能的输出之一
a 1
b 2
*/
/* 可能的输出之二
b 2
a 1
*/
遍历的时候元素的顺序是随机的,有可能先输出a,也有可能先输出b
在遍历中增加元素
在遍历的同时增加元素,增加的元素是可以被遍历到的
func main() {
m := map[string]int{
"a": 1,
"b": 2,
}
for k, v := range m {
m["c"] = 3
fmt.Println(k, v)
}
}
/* 输出
b 2
c 3 // 这个是在遍历中增加的元素,
a 1
*/
在遍历中删除元素
如果还没有遍历到某个元素的时候,就在遍历的过程中删除这个元素,那么后续的遍历是遍历不到这个元素的
func main() {
m := map[string]int{
"a": 1,
"b": 2,
}
n := 0
flag := false
for k, v := range m {
if !flag {
flag = true
if k == "a" {
delete(m, "b")
} else {
delete(m, "a")
}
}
n++
fmt.Println(n, k, v)
}
}
/* 可能的输出之一
1 a 1
*/
/* 可能的输出之二
1 b 2
*/
map的的遍历是随机的,因此上面的代码的作用是,如果第一次遍历的元素是a,那么删除b;如果第一次遍历的元素是b,那么删除a。两种可能的情况下,都只有一个输出,这说明,在遍历的时候删除了元素,那么后续就遍历不到了。
map遍历易错点
使用for range遍历的时候,k,v使用的同一块内存,这是容易出现错误的地方
func main() {
m := map[string]int{
"a": 1,
"b": 2,
}
sKey := []string{}
sValue := []*int{}
{
fmt.Println("---- range ---- start")
for k, v := range m {
fmt.Printf("key:%p,%v, value:%p,%v\n", &k, k, &v, v)
sKey = append(sKey, k) // 保存的是k的值
sValue = append(sValue, &v) // 保存的是v的指针
}
fmt.Println("---- range ---- end\n")
}
{
// 打印出 sKey 中保存的元素,以及实际指向的值
fmt.Println("---- sKey ---- start")
fmt.Printf("%p, %v\n", &sKey[0], sKey[0])
fmt.Printf("%p, %v\n", &sKey[1], sKey[1])
fmt.Println("---- sKey ---- end\n")
}
{
// 打印出 sValue 中保存的元素,以及实际指向的值
fmt.Println("---- sValue ---- start")
fmt.Printf("%v, %v\n", sValue[0], *sValue[0])
fmt.Printf("%v, %v\n", sValue[1], *sValue[1])
fmt.Println("---- sValue ---- end\n")
}
}
/* 输出
---- range ---- start
// 四列分别为:k的地址,k的值,v的地址,v的值
key:0xc0000521f0,b, value:0xc0000180f0,2
key:0xc0000521f0,a, value:0xc0000180f0,1
---- range ---- end
---- sKey ---- start
// 两列分别为:sKey[i]的地址, sKey[i]的值
0xc0000044a0, b
0xc0000044b0, a
---- sKey ---- end
---- sValue ---- start
// 三列分别为:sValue[i]的地址,sValue[i]的值,sValue[i]指针指向的值
0xc000052230, 0xc0000180f0, 1
0xc000052238, 0xc0000180f0, 1
---- sValue ---- end
*/
- 在遍历的时候,输出的map里面的k,v的值都是正常的
- 在遍历的时候,输出的range的k,v两个量的内存分别指向同一处,如k的内存地址,一直都是0xc0000521f0
在遍历的同时,分别保存了k,v的每次遍历的内容到一个slice中,不同点在于,k保存的是值,而v保存的是指针
- 在输出 sKey 的时候,可以正确的输出结果。见: sKey 的输出
- 在输出 sValue 的时候,发现两次的输出结果是一样的。(这里就是出错的地方)
因为我们将元素加入到 sValue 中的时候,做了v 的取地址操作。前面提到,在使用range遍历map的时候,v一直使用的同一块内存,因此对v取地址后,加入到 sValue 中的值,一直都是一个,因此 sValue输出了错误的结果。
同样地,如果在遍历的时候,对k进行取地址后,保存到变量里面,如 sKey2 := []*string{}
里面,sKey2 的 输出也就出错了。
那么这里一个疑问就是,为什么对k直接加到到slice中,就不会有问题?
因为k在加入到 sKey 中的时候,保存的是k的副本的值,从 sKey 的输出的第一列可以看出,sKey 的两个元素的地址是不同的。而 v在取地址加入到 sValue中的时候,虽然也是 指针的副本,但是副本里面保存的值是v的指针的值,而这个值在遍历过程中一直是一个不会发生变化,因此出错
有疑问加站长微信联系(非本文作者)