golang for range原理(转载)
range是Golang提供的一种迭代遍历手段,可操作的类型有数组、切片、Map、channel等,实际使用频率非常高。
探索range的实现机制是很有意思的事情,这可能会改变你使用range的习惯。
2. 热身
按照惯例,我们看几个有意思的题目,用于检测对range的了解程度。
2.1 题目一:切片遍历
下面函数通过遍历切片,打印切片的下标和元素值,请问性能上有没有可优化的空间?
func RangeSlice(slice []int) {for index, value := range slice {_, _ = index, value}
}
程序解释:
函数中使用for-range对切片进行遍历,获取切片的下标和元素素值,这里忽略函数的实际意义。
参考答案:
遍历过程中每次迭代会对index和value进行赋值,如果数据量大或者value类型为string时,对value的赋值操作可能是多余的,可以在for-range中忽略value值,使用slice[index]引用value值。
2.2 题目二:Map遍历
下面函数通过遍历Map,打印Map的key和value,请问性能上有没有可优化的空间?
func RangeMap(myMap map[int]string) {for key, _ := range myMap {_, _ = key, myMap[key]}
}
程序解释:
函数中使用for-range对map进行遍历,获取map的key值,并根据key值获取获取value值,这里忽略函数的实际意义。
参考答案:
函数中for-range语句中只获取key值,然后根据key值获取value值,虽然看似减少了一次赋值,但通过key值查找value值的性能消耗可能高于赋值消耗。能否优化取决于map所存储数据结构特征、结合实际情况进行。
2.3 题目三:动态遍历
请问如下程序是否能正常结束?
func main() {v := []int{1, 2, 3}for i:= range v {v = append(v, i)}
}
程序解释:
main()函数中定义一个切片v,通过range遍历v,遍历过程中不断向v中添加新的元素。
参考答案:
能够正常结束。循环内改变切片的长度,不影响循环次数,循环次数在循环开始前就已经确定了。
3. 实现原理
对于for-range语句的实现,可以从编译器源码中找到答案。
编译器源码gofrontend/go/statements.cc/For_range_statement::do_lower()
方法中有如下注释。
// Arrange to do a loop appropriate for the type. We will produce
// for INIT ; COND ; POST {
// ITER_INIT
// INDEX = INDEX_TEMP
// VALUE = VALUE_TEMP // If there is a value
// original statements
// }
可见range实际上是一个C风格的循环结构。range支持数组、数组指针、切片、map和channel类型,对于不同类型有些细节上的差异。
3.1 range for slice
下面的注释解释了遍历slice的过程:
// The loop we generate:
// for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
遍历slice前会先获取slice的长度len_temp作为循环次数,循环体中,每次循环会先获取元素值,如果for-range中接收index和value的话,则会对index和value进行一次赋值。
由于循环开始前循环次数就已经确定了,所以循环过程中新添加的元素是没办法遍历到的。
另外,数组与数组指针的遍历过程与slice基本一致,不再赘述。
3.2 range for map
下面的注释解释了遍历map的过程:
// The loop we generate:
// var hiter map_iteration_struct
// for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
// index_temp = *hiter.key
// value_temp = *hiter.val
// index = index_temp
// value = value_temp
// original body
// }
遍历map时没有指定循环次数,循环体与遍历slice类似。由于map底层实现与slice不同,map底层使用hash表实现,插入数据位置是随机的,所以遍历过程中新插入的数据不能保证遍历到。
3.3 range for channel
遍历channel是最特殊的,这是由channel的实现机制决定的:
// The loop we generate:
// for {
// index_temp, ok_temp = <-range
// if !ok_temp {
// break
// }
// index = index_temp
// original body
// }
channel遍历是依次从channel中读取数据,读取前是不知道里面有多少个元素的。如果channel中没有元素,则会阻塞等待,如果channel已被关闭,则会解除阻塞并退出循环。
注:
- 上述注释中index_temp实际上描述是有误的,应该为value_temp,因为index对于channel是没有意义的。
- 使用for-range遍历channel时只能获取一个返回值。
4. 编程Tips
- 遍历过程中可以视情况放弃接收index或value,可以一定程度上提升性能
- 遍历channel时,如果channel中没有数据,可能会阻塞
- 尽量避免遍历过程中修改原数据
5. 总结
- for-range的实现实际上是C风格的for循环
- 使用index,value接收range返回值会发生一次数据拷贝
golang for range原理(转载)相关推荐
- Golang sync.Map 原理(两个map实现 读写分离、适用读多写少场景)
参考: 由浅入深聊聊Golang的sync.Map 通过对源码的逐行分析,清晰易懂 Golang sync.Map原理 通过向 sync.Map 中增删改查来介绍sync.Map的底层原理 Golan ...
- Golang的range
range 是 golang中特别常用的一种遍历方式,走C++入门的看到这样的遍历方式感觉太好用了.但是如果没有认真思考过range的工作原理,在一些特定的场景使用range,可能并不能达到预期的效果 ...
- linux+mmap父子通信_linux库函数mmap()原理?转载
linux库函数mmap()原理 转载 1.mmap基本概念 2.mmap内存映射原理 3.mmap和常规文件操作的区别 4.mmap优点总结 5.mmap相关函数 6.mmap使用细节 7.mmap ...
- Golang Context 详细原理和使用技巧
文章目录 Golang Context 详细原理和使用技巧 Context 背景 和 适用场景 Context 的背景 Context 的功能和目的 Context 的基本使用 Context 的同步 ...
- GoLang定时器实现原理
简介 工作中经常有定时执行某些代码块的需求,如果是PHP代码,一般写个脚本,然后用Cron实现. Go里提供了两种定时器:Timer(到达指定时间触发且只触发一次)和 Ticker(间隔特定时间触发) ...
- Golang sync.Map原理
原生map的"先天不足" 对于已经初始化了的原生map,我们可以尽情地对其进行并发读: package mainimport ("fmt""math/ ...
- ConcurrentHashMap实现原理--转载
原文地址:http://ajax-xu.iteye.com/blog/1104649 ConcurrentHashMap是Java 5中支持高并发.高吞吐量的线程安全HashMap实现.在这之前我对C ...
- java原子操作的实现原理--转载
原文地址:http://www.infoq.com/cn/articles/atomic-operation 1. 引言 原子(atom)本意是"不能被进一步分割的最小粒子",而原 ...
- JDK动态代理实现原理--转载
之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白.比如说:InvocationHandler的invoke方法是由谁来调用的,代理对象是怎么生成的,直到前几个星期才把这些问题全部搞明白了. ...
最新文章
- 企业级 SpringBoot 教程 (十九) 验证表单信息
- golang 数组和切片
- C运算符解析及优先级
- winform绑定多张图片
- Elasticsearch--分布式RESTful搜索引擎
- MFC DoDataExchange()绑定技术
- Spring框架之演示JDBC的模板类
- 机器学习入门:隐马尔科夫模型-8
- 计算机流体仿真,ANSYS FLUENT 计算流体力学软件
- 运维 之 常用运维工具
- 生产排程系统_【PSI系统】在生产中进行更智慧的详细计划:计划排程工具APS是否值得企业投资?...
- CentOS通过DNSpod实现DDNS动态域名,在家也可以搭建主机服务器了
- Istio 东西向流量管理
- Photoshop(PS)CC2020安装教程【64位】
- 读书笔记——好句摘抄
- 《C Primer Plus》学习笔记—第9章
- CAP理论举例及说明
- sql获取group by最后一条记录
- nico老是显示服务器升级,Nico会员服务条款
- 什么是邮箱地址?邮箱地址在哪里找?