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 时还有其他疑问,欢迎留言讨论。


    目录
    相关文章
    |
    10月前
    |
    存储 JSON 安全
    Go语言切片,使用技巧与避坑指南
    Go语言中的切片(Slice)是动态引用数组的高效数据结构,支持扩容与截取。本文从切片基础、常用操作到高级技巧全面解析,涵盖创建方式、`append`扩容机制、共享陷阱及安全复制等内容。通过代码示例详解切片特性,如预分配优化性能、区分`nil`与空切片、处理多维切片等。掌握这些核心知识点,可编写更高效的Go代码。
    395 2
    |
    存储 Go 索引
    go语言中数组和切片
    go语言中数组和切片
    344 7
    |
    Go 索引
    Go语言中,遍历数组或切片
    在Go语言中,遍历数组或切片
    315 6
    |
    9月前
    |
    数据采集 机器学习/深度学习 存储
    Go语言实战案例 - 找出切片中的最大值与最小值
    本案例通过实现查找整数切片中的最大值与最小值,帮助初学者掌握遍历、比较和错误处理技巧,内容涵盖算法基础、应用场景及完整代码示例,适合初学者提升编程能力。
    |
    测试技术 Go API
    Go 切片导致 rand.Shuffle 产生重复数据的原因与解决方案
    在 Go 语言开发中,使用切片时由于其底层数据共享特性,可能会引发意想不到的 Bug。本文分析了 `rand.Shuffle` 后切片数据重复的问题,指出原因在于切片是引用类型,直接赋值会导致底层数组共享,进而影响原始数据。解决方案是使用 `append` 进行数据拷贝,确保独立副本,避免 `rand.Shuffle` 影响原始数据。总结强调了切片作为引用类型的特性及正确处理方法,确保代码稳定性和正确性。
    344 82
    |
    10月前
    |
    Go 索引
    Go语言中使用切片需要注意什么?
    本文详细讲解了Go语言中切片(Slice)的使用方法与注意事项。切片是对数组连续片段的引用,具有灵活的操作方式。文章从定义与初始化、长度与容量、自动扩容、共享底层数组、复制、边界检查、零值到拼接等方面展开,并配以示例代码演示。通过学习,读者可深入了解切片的工作原理及优化技巧,避免常见陷阱,提升编程效率与代码质量。
    245 2
    |
    11月前
    |
    安全 Go 开发者
    Go语言之切片的原理与用法 - 《Go语言实战指南》
    切片(slice)是Go语言中用于处理变长数据集合的核心结构,基于数组的轻量级抽象,具有灵活高效的特点。切片本质是一个三元组:指向底层数组的指针、长度(len)和容量(cap)。本文详细介绍了切片的声明与初始化方式、基本操作(如访问、修改、遍历)、长度与容量的区别、自动扩容机制、共享与副本处理、引用类型特性以及常见陷阱。通过理解切片的底层原理,开发者可以更高效地使用这一数据结构,优化代码性能。
    395 13
    |
    11月前
    |
    人工智能 Go
    [go]Slice 切片原理
    本文详细介绍了Go语言中的切片(slice)数据结构,包括其定义、创建方式、扩容机制及常见操作。切片是一种动态数组,依托底层数组实现,具有灵活的扩容和传递特性。文章解析了切片的内部结构(包含指向底层数组的指针、长度和容量),并探讨了通过`make`创建切片、基于数组生成切片以及切片扩容的规则。此外,还分析了`append`函数的工作原理及其可能引发的扩容问题,以及切片拷贝时需要注意的细节。最后,通过典型面试题深入讲解了切片在函数间传递时的行为特点,帮助读者更好地理解和使用Go语言中的切片。
    357 0
    |
    存储 Go
    Go 语言入门指南:切片
    Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
    397 3
    Go 语言入门指南:切片
    |
    Go 索引
    go语言for遍历数组或切片
    go语言for遍历数组或切片
    426 62

    热门文章

    最新文章

    下一篇
    开通oss服务