range 可以用来很方便地遍历数组(array)、切片(slice)、字典(map)和信道(chan)
//变量 words 在循环开始前,仅会计算一次,如果在循环中修改切片的长度不会改变本次循环的次数。//迭代过程中,每次迭代的下标和值被赋值给变量 i 和 s,第二个参数 s 是可选的。//针对 nil 切片,迭代次数为 0
words :=[]string{"Go","语言","高性能","编程"}for i, s :=range words {
words =append(words,"test")
fmt.Println(i, s)}//遍历map//和切片不同的是,迭代过程中,删除还未迭代到的键值对,则该键值对不会被迭代。//在迭代过程中,如果创建新的键值对,那么新增键值对,可能被迭代,也可能不会被迭代。//针对 nil 字典,迭代次数为 0
m :=map[string]int{"one":1,"two":2,"three":3,}for k, v :=range m {delete(m,"two")
m["four"]=4
fmt.Printf("%v: %v\n", k, v)}//遍历channel//发送给信道(channel) 的值可以使用 for 循环迭代,直到信道被关闭。//如果是 nil 信道,循环将永远阻塞
ch :=make(chanstring)gofunc(){
ch <-"Go"
ch <-"语言"
ch <-"高性能"
ch <-"编程"close(ch)}()for n :=range ch {
fmt.Println(n)}
二. for 和 range 的性能比较
仅遍历下标的情况下,for 和 range 的性能几乎是一样,如
funcBenchmarkForIntSlice(b *testing.B){
nums :=generateWithCap(1024*1024)for i :=0; i < b.N; i++{len:=len(nums)var tmp intfor k :=0; k <len; k++{
tmp = nums[k]}_= tmp
}}funcBenchmarkRangeIntSlice(b *testing.B){
nums :=generateWithCap(1024*1024)for i :=0; i < b.N; i++{var tmp intfor_, num :=range nums {
tmp = num
}_= tmp
}}
type Item struct{
id int
val [4096]byte}funcBenchmarkForStruct(b *testing.B){var items [1024]Item
for i :=0; i < b.N; i++{
length :=len(items)var tmp intfor k :=0; k < length; k++{
tmp = items[k].id
}_= tmp
}}funcBenchmarkRangeIndexStruct(b *testing.B){var items [1024]Item
for i :=0; i < b.N; i++{var tmp intfor k :=range items {
tmp = items[k].id
}_= tmp
}}funcBenchmarkRangeStruct(b *testing.B){var items [1024]Item
for i :=0; i < b.N; i++{var tmp intfor_, item :=range items {
tmp = item.id
}_= tmp
}}
[]int 和 []struct{} 的性能差异
与 for 不同的是,range 对每个迭代值都创建了一个拷贝。因此如果每次迭代的值内存占用很小的情况下,for 和 range 的性能几乎没有差异,但是如果每个迭代值内存占用很大,例如上面的例子中,每个结构体需要占据 4KB 的内存,这种情况下差距就非常明显了
证明 range 迭代时,返回的是拷贝
//persons 是一个长度为 3 的切片,每个元素是一个结构体。//使用 range 迭代时,试图将每个结构体的 no 字段增加 10,但修改无效,因为 range 返回的是拷贝。//使用 for 迭代时,将每个结构体的 no 字段增加 100,修改有效
persons :=[]struct{ no int}{{no:1},{no:2},{no:3}}for_, s :=range persons {
s.no +=10}for i :=0; i <len(persons); i++{
persons[i].no +=100}
fmt.Println(persons)// [{101} {102} {103}]
[]*struct{}指针
切片元素从结构体 Item 替换为指针 *Item 后,for 和 range 的性能几乎是一样的。而且使用指针还有另一个好处,可以直接修改指针对应的结构体的值