西安网站开发托管代运营,查公司资质在哪个网站,网站空间永久免费,太原建设厅官方网站前言
基础语法不再赘述#xff0c;写这个原因是之前的某次面试被问道了#xff0c;我知道会导致问题但具体答下来不是很通顺。再回想自己开发过程中#xff0c;很多地方都是使用到了for/for range#xff0c;但是却从没注意过一些细节#xff0c;因此专门学习一下进行记录…前言
基础语法不再赘述写这个原因是之前的某次面试被问道了我知道会导致问题但具体答下来不是很通顺。再回想自己开发过程中很多地方都是使用到了for/for range但是却从没注意过一些细节因此专门学习一下进行记录。 对一个数组循环for range使用kv时候有什么要注意的吗 这个是当时面经记录的问题。因此顺着这里开始进行学习。 for和for range基本语法
for的用法大概可以类比C里面的for和while
//类似C的for
for i : 0; i 10; i {fmt.Println(i)
}
//类似C的while
j : 0
for j 10 {fmt.Println(j)j
}//死循环
for {fmt.Println(无限循环)
}fo range用法大概可以类比python的range。但是golang中他的用法更多大致四种
//遍历数组
numbers : [3]int{1, 2, 3}
for index, value : range numbers {fmt.Printf(索引%d 值%d\n, index, value)
}
//遍历切片
names : []string{Alice, Bob, Charlie}
for index, name : range names {fmt.Printf(索引%d 值%s\n, index, name)
}
//遍历 map
ages : map[string]int{Alice: 25, Bob: 30, Charlie: 35}
for name, age : range ages {fmt.Printf(%s 的年龄是 %d 岁\n, name, age)
}
//遍历字符串
sentence : Hello, 世界
for index, char : range sentence {fmt.Printf(索引%d 字符%c\n, index, char)
}for和for range的区别
对于一个数组例如a[3]{3.2.1}可以通过两种方式进行遍历 func main() {fmt.Println(下面是for range遍历)var m []int{1, 2, 3}for k, v : range m {fmt.Println(k, v)fmt.Println(k, v)}fmt.Println(下面是for遍历)for i : 0; i 3; i {fmt.Println(i, m[i])fmt.Println(i, m[i])}
}
其结果如下 我们可以发现输出的结果是一样的但地址却不一样 因此这便是二者的本质区别—— 1、range遍历在开始遍历数据之前会先拷贝一份被遍历的数据。所以在遍历过程中去修改被遍历的数据只是修改拷贝的数据不会影响到原数据。 2、range在遍历值类型时其中的v是一个局部变量只会声明初始化一次之后每次循环时重新赋值覆盖前面的。
常见的坑
通过指针取值
//此代码来自https://zhuanlan.zhihu.com/p/105435646
arr : [2]int{1, 2}
res : []*int{}
for _, v : range arr {res append(res, v)
}
//expect: 1 2
fmt.Println(*res[0],*res[1])
//but output: 2 2这个是由于上述的区别2——v是一个只声明一次的局部变量。 修改方案两种1、通过arr[k]获取值而不是v。 2、使用一个局部变量tmp :v之后对tmp处理。
循环中添加元素是否会导致死循环
func main() {s : []int{0, 1}for _, v : range s {s append(s, v)}fmt.Printf(s%v\n, s)
}大致意思是这样会不会死循环 这个是上面的性质1。range因为是对复制的数据在操作所以不会影响。普通的for则会影响。 https://cloud.tencent.com/developer/article/1925475 此部分具体参考此例即可。 对大数据的操作
对于很大数据的数组若采取range遍历因为复制的缘故所以会导致开销巨大。具体见下的性能分析 但是在大数据的数组下使用range去重置全部赋值为0效率是高的。原因是内存是连续的编译器会直接清空这一片的内存。具体讲解见此
demo和源码看这里的3和4
对于map的操作
具体来说就是边遍历边新增/删除。删除了的不可能再遍历到新增的可能再遍历到不一定。
原因map内部实现是一个链式hash表为了保证无顺序初始化时会随机一个遍历开始的位置所以新增的元素被遍历到就变的不确定了同样删除也是一个道理但是删除元素后边就不会出现所以一定不会被遍历到。
具体参考此文的最后部分
range中开goroutine
func main() {var m []int{1, 2, 3}for i : range m {go func() {fmt.Print(i)}()}time.Sleep(time.Millisecond)}会发现输出结果有随机性发现结果是222。原因是上述的那个k、v是同一值他并没有保存到goroutine的栈中。 解决方法还是局部变量但可能出现012、210、102这样的值…原因是运行太快了所以顺序不一定…
附1range遍历的性能分析
for range需要进行一步复制操作因此显然需要比for更多的性能开销。但是对于切片来说因为切片的副本和原切片都是指向数组的地址所以性能一样。 具体见此文。 https://blog.csdn.net/EDDYCJY/article/details/124701572 附2一个坑中坑——map的range遍历并不是随机
具体参考此文
结论是第一位的元素会有更高的概率被首先选中。因此不可用map遍历的随机性作为随机选择map中元素的方法。
参考资源
Go 处理大数组使用 for range 还是 for 循环 Go的循环遍历使用小坑 Go 中for range的一个坑 Go语言中for-range使用踩坑指南 Dig101 - Go之for-range排坑指南 Golang 语言 for 和 for-range 的区别 5.1 for 和 range #