Go切片循环就用range 有这一篇就够了

简介: Go切片循环就用range 有这一篇就够了

/ Go 语言 range 关键字完全指南 /

range 是 Go 语言中非常重要的关键字,用来在各种数据结构上进行迭代,比如数组、切片、Map 等。其中,在切片上使用 range 进行循环迭代是非常常见的。

本文将全面介绍在切片上使用 range 的相关知识,包括:

  1. range 关键字的作用
  2. range 基本语法
  3. 循环切片的索引和值
  4. range 返回值数量
  5. 只遍历索引或值
  6. range 的原理分析
  7. range 的常见用法
  8. range 迭代 map
  9. range 的性能优化
  10. range 的一些坑

通过详细的示例,可以全面了解在 Go 语言切片上使用 range 的诸多技巧,掌握 range 关键字的精髓所在。相信本文将是你学习 range 的最佳指南。


1

 

1. range 关键字

range 关键字用于基于数组或切片等进行迭代,它可以返回每个迭代的索引和值。基本语法如下:

for idx, val := range coll {
}

range 返回集合的索引和值,通过这种方式可以进行各种迭代操作。


2

 

2. range 基本语法

对一个切片使用 range 进行迭代访问其元素:

func main() {
  nums := []int{10, 20, 30}
  for i, v := range nums {
    fmt.Println(i, v)
  }
}


输出

0 10 
1 20
2 30

range 默认返回两个值,索引和值。索引从 0 开始。

如果不需要索引,可以使用 _ 空标识符:

for _, v := range nums {
  fmt.Println(v) 
}

     

    3. 循环切片索引和值

    下面是切片 range 返回的索引和值的示例:

    func main() {
      fruits := []string{"apple", "banana", "orange"}
      for i, v := range fruits {
        fmt.Printf("index: %d, value: %s\n", i, v)
      }
    }

    输出:

    index: 0, value: apple
    index: 1, value: banana  
    index: 2, value: orange

    可以看出,range 可以同时返回切片元素的索引和值。

    一般 i 表示索引,v 表示值。根据需要决定是否忽略。


    4

     

    4. range 返回值数量

    range 有几种不同的返回值数量:

    for idx := range slice     // 只返回索引
    for _, value := range slice // 只返回值
    for idx, value := range slice // 返回索引和值

    可以根据需要选择返回一个或两个值。

    返回两个值可以同时获得索引和值信息。返回一个值可以减少不必要的变量。


    5

     

    5. 只遍历索引或值

    如果只需要索引,可以:

    for i := range slice {
      // 使用索引i
    }

    只需要值,使用空标识符 _:

    for _, v := range slice {
      // 使用值v
    }

    这在不同场景下都很常用。


    6

     

    6. range 的原理

    range 在底层是基于指针进行的迭代:

    for ptr, end := &slice[0], &slice[len(slice)]; ptr != end; ptr++ {
      // ptr是指向每个元素的指针    
    }

    range 做了这样的工作:

    1. 获取开始和结束指针
    2. 指针自增访问每个元素
    3. 返回索引和值

    理解这一点有助于分析 range 的行为。


    7

     

    7. range 常见用法

    range 的常见用法有下面这些:

    • 数组或切片遍历
    • Map 遍历
    • 字符串遍历
    • Channel 遍历

    7.1

     

    7.1 数组遍历

    func main() {
      nums := [5]int{1, 2, 3, 4, 5}
      for i, v := range nums {
        fmt.Println(i, v)
      }
    }

    数组也可以直接使用 range 进行遍历。

    7.2

     

    7.2 Map 遍历

    遍历 map 时,range 返回 key 和 value:

    func main() {
      kvs := map[string]string{"a": "apple", "b": "banana"}  
      for k, v := range kvs {
        fmt.Println(k, v) 
      }
    }

    遍历 map 时,每次遍历的顺序是不固定的。

    7.3

     

    7.3 字符串遍历

    字符串在 Go 里是字节切片,也可以用 range 迭代:

    遍历 map 时,每次遍历的顺序是不固定的。
    7.3
    7.3 字符串遍历
    字符串在 Go 里是字节切片,也可以用 range 迭代:

    需要转换成 string 打印,因为直接打印字节值。

    7.4

     

    7.4 Channel 遍历

    对一个 channel 使用 range 可以不断获取发送的值:

    func main() {
      ch := make(chan int)
      go func() {
        ch <- 1
        ch <- 2
        close(ch)  
      }()
      for v := range ch {
        fmt.Println(v)
      }
    }

    需要关闭 channel 来结束 range。

    这些是 range 最典型的用法。


    8

     

    8. range 迭代 map

    对 map 使用 range 有一些特殊行为需要注意。

    • 随机顺序:每次遍历顺序可能不一样
    • 仅值:range map 只返回值
    • 删除和并发:删除或并发读写可能导致 panic

    比如:

    func main() {
      m := map[string]int{"a": 1, "b": 2}
      for k, v := range m {
        println(k, v)
      }
    }

    多次运行会发现每次 k 的顺序都可能不同。

    如果需要固定顺序,需要先将 key 放到切片里排序。

    range 遍历 map 时如果同时在另一个 goroutine 删除 key,会导致 panic 异常。


    9

     

    9. range 优化

    range 在迭代切片和数组时会生成大量临时变量,可以通过以下方法优化:

    • 复用变量
    • 减少不必要变量
    • 提前计算长度
    • 首尾两段分别遍历
    • 使用指针直接遍历

    例如:

    var length = len(slice)
    for i := 0; i < length; i++ {
      value := slice[i]
      // 迭代元素 
    }

    这样可以减少变量生成,同时也能提高一些效率。


    10

     

    10. range 的坑

    range 虽然常用,但是容易踩到一些坑,主要有下面这些:

    • 范围变量重用问题
    • 循环变量逃逸导致错误
    • range channel 必须关闭
    • range map 随机顺序

    比如使用循环变量时:

    funcs := []func(){}
    for _, f := range funcs {
      go f() // 使用的是同一个f
    }

    要避免重用变量,最安全的方式是在循环里重新定义变量:

    要避免重用变量,最安全的方式是在循环里重新定义变量:

    11

     

    总结

    通过本文,我们全面介绍了 Go 语言中使用 range 迭代切片的相关内容。range 是 Go 语言中非常重要的一个设计,可以巧妙地在各种数据结构上进行遍历。理解 range 的设计思想和语法用法可以让我们更好地编写 Go 代码。如果你在使用 range 时还有其他疑问,欢迎留言讨论。


    目录
    相关文章
    |
    4天前
    |
    Go 索引
    go语言中的循环语句
    【11月更文挑战第4天】
    13 2
    |
    16天前
    |
    存储 Go
    |
    17天前
    |
    Java Go 数据处理
    go语言使用切片而非数组
    【10月更文挑战第18天】
    9 1
    |
    15天前
    |
    Go
    |
    29天前
    |
    存储 安全 Go
    Go语言切片:从入门到精通的深度探索###
    本文深入浅出地剖析了Go语言中切片(Slice)这一核心概念,从其定义、内部结构、基本操作到高级特性与最佳实践,为读者提供了一个全面而深入的理解。通过对比数组,揭示切片的灵活性与高效性,并探讨其在并发编程中的应用优势。本文旨在帮助开发者更好地掌握切片,提升Go语言编程技能。 ###
    |
    2月前
    |
    Go 索引
    Go to Learn Go之切片
    Go to Learn Go之切片
    28 1
    Go语言的条件控制语句及循环语句的学习笔记
    本文是Go语言的条件控制语句和循环语句的学习笔记,涵盖了if语句、if-else语句、if嵌套语句、switch语句、select语句以及for循环和相关循环控制语句的使用方法。
    Go语言的条件控制语句及循环语句的学习笔记
    |
    2月前
    |
    编译器 Go 索引
    Go数组、多维数组和切片(动态数组),及常用函数len(),cap(),copy(),append()在切片中的使用
    本文介绍了Go语言中数组、多维数组和切片(动态数组)的基本概念和操作,包括数组的定义、初始化、访问,多维数组的定义和访问,以及切片的创建、使用和扩容。同时,还讲解了切片中常用的函数len()、cap()、copy()和append()的使用方法。
    |
    3月前
    |
    存储 编译器 Go
    |
    3月前
    |
    Go
    Go 1.21的新特性: 切片和映射
    Go 1.21的新特性: 切片和映射