range 是Go语言提供的一种遍历手段,可以操作的类型有Array, Slice, Map, Channel.
遍历切片时使用正确的姿势是通过索引取值
Bad | Good |
---|---|
|
|
函数中使用for-range对切片进行遍历,获取切片的下标和元素素值,这里忽略函数的实际意义。
遍历过程中每次迭代会对index和value进行赋值,如果数据量大或者value类型为string时,对value的赋值操作可能是多余的,可以在for-range中忽略value值,建议使用slice[index]引用value值。
正确的遍历Map, 和slice刚好不同,不建议在遍历map的时候通过key取出value的值
Bad | Good |
---|---|
|
|
动态遍历,所谓动态遍历就是说,在遍历目标类型时,类型自身也在改变。
func main() {
v := []int{1, 2, 3}
for i:= range v {
v = append(v, i)
}
}
可以正常结束。 许婚内改变切片的长度,不影响循环的次数,循环次数在循环开始前就已经确定了。
对于for...range语句的实现,编译器的源码gofrontend/go/statements.cc/For_range_statement::do_lower()
方法有如下解释。
go // 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 // }
而针对每一种可遍历的数据类型定义了不同的实现。
// 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
// }
由于循环开始前循环次数就已经确定了,所以循环过程中新添加的元素是没办法遍历到的。
源码中注释如下
// 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表实现,插入数据位置是随机的,所以遍历过程中新插入的数据不能保证遍历到。
map遍历是随机的,导致动态追加的key-value并不能保证能被遍历到
遍历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关闭,则会解除阻塞并退出循环
注意:
原文:https://www.cnblogs.com/failymao/p/14939539.html