核心结论:for range遍历的是拷贝的对象,不是原对象
for range 会拷贝遍历的对象,然后遍历这个拷贝的新对象
-
for range 引用型对象
map, slice本身是引用型的对象,for range拷贝的就当然只是引用,或者说header。在for range中对map, slice的原对象进行修改,有可能对遍历的结果产生影响,因为原对象和拷贝的、被遍历的对象,指向同一份底层数据
for range map:
// ###for range map###
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
var m1 = make(map[int]int)
m1[1] = 1
m1[2] = 2
m1[3] = 3
// 可以正常删除当前访问的k v,不影响对map的完整遍历
for key, val := range m1 {
fmt.Println(key, val)
delete(m1, key)
}
fmt.Println("final map len: ", len(m1)) // 0
// for range 内部对map新增k v或者删除k v
// 新增的k v不一定能被遍历到,因为可能这对kv插入了已经iter过的bucket中
var m = map[int]int{1: 1, 2: 2, 3: 3}
for i, _ := range m {
m[i+3] = i + 3
fmt.Printf("%d%d ", i, m[i])
}
}
for range slice1:
// ###for range slice1###
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
// for range s 会拷贝 s 得到s',而 len(s') = 3
// ele是遍历s'得到的,遍历发生在s'上,因此即使在for range内不断对s append也不会产生无限循环
s := []int{1, 2, 3}
for _, ele := range s {
fmt.Println(ele)
s = append(s, ele+3)
}
fmt.Println(s) // [1 2 3 4 5 6]
}
// 1
// 2
// 3
// [1 2 3 4 5 6]
for range slice2:
// ###for range slice2###
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
// for range s 会拷贝 s 得到s',而 len(s‘) = 5
// range的对象实际上是s的一份拷贝s',s'和s共同引用一个底层数组
// 因此即使在for range中对s不断截断,但是s'仍然引用了底层数组的{1, 2, 3, 4, 5}部分
s := []int{1, 2, 3, 4, 5}
for _, ele := range s {
s = s[:len(s)-1] // 截断s
fmt.Println("current s:", s)
fmt.Println("visited ele:", ele)
fmt.Println("---")
}
}
// current s: [1 2 3 4]
// visited ele: 1
// ---
// current s: [1 2 3]
// visited ele: 2
// ---
// current s: [1 2]
// visited ele: 3
// ---
// current s: [1]
// visited ele: 4
// ---
// current s: []
// visited ele: 5
// ---
-
for range 值型对象
array是值类型的对象,for range会进行值拷贝。在for range中对array原对象进行修改,不会对遍历的结果产生影响,因为原对象和拷贝的、被遍历的对象,是两个独立的对象
// ###for range slice###
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
// for range s 会拷贝 s 得到s',而 len(s') = 5
// range的对象实际上是s的一份拷贝s',s'和s是两个独立的数组,不会相互影响
// 因此即使在for range中对s进行修改,但s'的值不会被影响,遍历得到的仍然是1 2 3 4 5
s := [5]int{1, 2, 3, 4, 5}
for index, ele := range s {
if index < 4 {
s[index+1] = 100 + s[index]
}
fmt.Println("original s:", s)
fmt.Printf("visited range index: %d, visited range ele: %d\n", index, ele)
fmt.Println("---")
}
}
// original s: [1 101 3 4 5]
// visited range index: 0, visited range ele: 1
// ---
// original s: [1 101 201 4 5]
// visited range index: 1, visited range ele: 2
// ---
// original s: [1 101 201 301 5]
// visited range index: 2, visited range ele: 3
// ---
// original s: [1 101 201 301 401]
// visited range index: 3, visited range ele: 4
// ---
// original s: [1 101 201 301 401]
// visited range index: 4, visited range ele: 5
// ---
网友评论