前言
for i:通过下标迭代。
for range:通过值拷贝迭代。
本文主要说明通过 for i 和 for range 迭代数据结构的性能差异。
注意:以下 1 和 2 两种迭代都是通过下标迭代,3 或 4 才是通过值拷贝迭代。
// 1
for i := 0; i < len(nums); i++ {...}
// 2
for i := range nums
// 3
for _, num := range nums
// 4
for i, num := range nums
差异
一、for i 迭代能够修改数据,而 for range 迭代不能。
举个例子证明:
type Person struct {
Name string
age int
}
func main() {
students := []Person{
{Name: "zwjason", age: 21},
{Name: "bakanrian", age: 22},
}
// for range 尝试修改
for _, student := range students {
student.age = 20
}
for _, student := range students {
fmt.Printf("%s %d\n", student.Name, student.age)
}
// for i 尝试修改
for i := range students {
students[i].age = 20
}
for _, student := range students {
fmt.Printf("%s %d\n", student.Name, student.age)
}
}
输出如下:
zwjason 21
bakanrian 22 // 修改失败
zwjason 20
bakanrian 20 // 修改成功
可以看到通过 for range 迭代修改失败,因为它是值拷贝迭代,student 只是复制了对应的值。
而 for i 迭代修改成功,因为它是直接对原数组操作。
二、当迭代的值占内存较大时,for range 的性能急剧下降。
比如当时用 for range 迭代如下数据结构:
type Person struct {
Name string
scores [4096]int
}
忽略 Name,一个 Person 实例占用内存至少为 84096 = 32 KB 。
原因是每次迭代都要拷贝至少 32 KB 的数据。
使用指针类型 []Person 可以避免这个问题,因为复制一个指针的代价一般只有 64 B (在 64 位机器)。