Go-map、切片、数组循环常见问题总结

开发人员在日常开发中经常会遇到for range循环需求,因其类型的特殊性,用起来有别于其他语言,需要注意,这里对map、切片、数组循环遇到常见问题进行整理。

map

1、for range map 在开始执行循环的时候,底层做了随机种子,故其循环是随机的。

package main
import "fmt"
func main() {
	a := map[int]int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	for _, c := range a {
		fmt.Println(c)
	}
}

输出:
3
4
5
1
2
多次执行,结果不同

数组

package main

import "fmt"

func main() {

	a := [5]int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	for b, c := range a {
		if b == 0 {
			a[1] = 22
			//fmt.Println(b)
		}
		a[b] = c + 10
	}
	fmt.Println(a)
}

输出:
[11 12 13 14 15]

//a[1] = 22 并没有起作用,因数组是值类型,使用range获取的数组项是复制过来的,并非引用原始数据。

如果想a[1] = 22 有效,只需改成引用。

package main

import "fmt"

func main() {

	a := [5]int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	
	for b, c := range &a {//注意此处改变
		if b == 0 {
			a[1] = 22
			//fmt.Println(b)
		}
		a[b] = c + 10
	}
	fmt.Println(a)
}

结果:
[11 32 13 14 15]

slice

slice 是引用类型,底层指针指向数组,传参的时候传引用和传值区别不大。

示例1
package main

import "fmt"

func main() {

	a := []int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	for b, c := range a {
		if b == 0 {
			a[1] = 22
			//fmt.Println(b)
		}
		a[b] = c + 10
	}
	fmt.Println(a)
}

结果:
[11 32 13 14 15]
对比上一个例子,不需要传引用就可以更改a[1] = 22

示例2

这个是输出异常的示例

package main

import (
"fmt"
)

func main() {

	a := []int{1, 2, 3}
	b := make([]*int, len(a))

	for i, v := range a {
		//解决办法1增加临时变量
		//vv := v
		//b[i] = &vv
		//解决办法2   
		//b[i] = &a[i]
		b[i] = &v
	}
	for _, v := range b {
		fmt.Println(*v)
	}
}

输出:
3
3
3

原因v变量在for range中只会创建一次,之后迭代重复使用

迭代闭包容易出问题

package main

import (
	"fmt"
	"time"
)

func main() {
	a := []int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	for _, c := range a {
	
		go func() {
			fmt.Println(c)
		}()
	}
	time.Sleep(3 * time.Second)

}

结果为:
3
3
5
5
5
有两种方法解决以上问题

1、增加一个临时变量

package main

import (
	"fmt"
	"time"
)

func main() {
	a := []int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	for _, c := range a {
		cc:=c //增减临时变量cc
		go func() {
			fmt.Println(cc)
		}()
	}
	time.Sleep(3 * time.Second)

}

2、 传参进去

package main

import (
	"fmt"
	"time"
)

func main() {
	a := []int{0: 1, 1: 2, 2: 3, 3: 4, 4: 5}
	for _, c := range a {
		go func(c int) {
			fmt.Println(c)
		}(c)
	}
	time.Sleep(3 * time.Second)

}

循环子协程

package main

import (
	"fmt"
	"time"
)

type v struct {
	value int
}

func (vv *v) show() {
	fmt.Println(vv.value)
}
func main() {
	a := []v{{1}, {2}, {3}}
	for _, c := range a {
		go c.show()
	}
	time.Sleep(3 * time.Second)
}

输出:
3
3
3
解决办法
1、增加临时变量

package main

import (
	"fmt"
	"time"
)

type v struct {
	value int
}

func (vv *v) show() {
	fmt.Println(vv.value)
}
func main() {
	a := []v{{1}, {2}, {3}}
	for _, c := range a {
		c := c //增加这一行
		go c.show()
	}
	time.Sleep(3 * time.Second)
}

2、使用指针

package main

import (
	"fmt"
	"time"
)

type v struct {
	value int
}

func (vv *v) show() {
	fmt.Println(vv.value)
}
func main() {
	a := []*v{{1}, {2}, {3}}
	for _, c := range a {
		go c.show()
	}
	time.Sleep(3 * time.Second)
}

3、方法show接受者不用指针

package main

import (
	"fmt"
	"time"
)

type v struct {
	value int
}

func (vv v) show() {
	fmt.Println(vv.value)
}
func main() {
	a := []v{{1}, {2}, {3}}
	for _, c := range a {
		go c.show()
	}
	time.Sleep(3 * time.Second)
}

你可能感兴趣的:(开源,go,golang,数据结构,算法,for,range)