Go语言中范围range的使用小结

作者:

文章目录
  • range 是 Go 里 用于遍历数据结构的语法糖,常见用法: for k, v := range collection { … } 它能遍历的对象包括: 数组 / 切片 字符串 map channel 本质上:每次循环,range 会返回“索引/键 + 值”
  • arr := []int{10, 20, 30} for i, v := range arr { fmt.Println(i, v) } 输出: 0 101 202 30 只要值,不要索引 for _, v := range arr { fmt.Println(v) } 只要索引 for i := range arr { fmt.Println(i) }
  • s := “你好,go!” for i, c := range s { fmt.Printf(“index=%d,char=%cn”, i, c) } 输出: index=0,char=你index=3,char=好index=6,char=,index=9,char=gindex=10,char=oindex=11,char=! 注意: i:字节索引(不是字符索引) c:rune(Unicode 字符) 对比 Java Java Go char 是 UTF-16 rune 是 UTF-32 String.length() 不等于字符数 len(s) 是字节数 遍历字符较绕 range 天然支持 Unicode 遍历中文,Go 用 range 是“正解”
  • m := map[string]int{ “a”: 1, “b”: 2, } for k, v := range m { fmt.Println(k, v) } 重要特性 map 遍历 无序 每次运行顺序可能不一样 Go 刻意这么设计(防止依赖顺序的 bug) 如果需要有序,需这样处理: keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { fmt.Println(k, m[k]) }
  • 在并发中很有用 ch := make(chan int) go func() { ch <- 1 ch <- 2 close(ch) }() for v := range ch { fmt.Println(v) } 特点: 会 一直阻塞 直到 channel 被 close 非常适合消费协程产生的数据
  • 问题代码 arr := []int{1, 2, 3} for _, v := range arr { v = v * 10 } fmt.Println(arr) // [1 2 3] 为什么没改? v 是 元素的拷贝 修改 v 不影响原数组 正确写法 for i := range arr { arr[i] *= 10 }
  • 场景 建议 需要修改原数组 用索引 for 需要精确控制步长 用经典 for 性能极限场景 手写 for 更可控 遍历 map 顺序敏感 不适合
  • Go 的 range = 更安全、更简洁的 for-each,但要记住: 1. 值是拷贝; 2. map 无序; 3. 字符串按 rune。 到此这篇关于Go语言中范围range的使用小结的文章就介绍到这了,更多相关Go range内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Go语言范围Range的实现示例 Go语言范围Range的具体使用
  • 目录
    • 一、什么是range
    • 二、遍历数组 / 切片(最常用)
    • 三、遍历字符串
    • 四、遍历 map
    • 五、遍历 channel
    • 六、range 的“值拷贝陷阱”
    • 七、什么时候不用 range?
    • 八、总结

    range 是 Go 里 用于遍历数据结构的语法糖,常见用法:

    for k, v := range collection {
        ...
    }
    

    它能遍历的对象包括:

    • 数组 / 切片
    • 字符串
    • map
    • channel

    本质上:每次循环,range 会返回“索引/键 + 值”

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

    输出:

    0 10
    1 20
    2 30

    只要值,不要索引

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

    只要索引

    for i := range arr {
        fmt.Println(i)
    }
    

    s := "你好,go!"
    for i, c := range s {
    	fmt.Printf("index=%d,char=%cn", i, c)
    }

    输出:

    index=0,char=你
    index=3,char=好
    index=6,char=,
    index=9,char=g
    index=10,char=o
    index=11,char=!

    注意:

    • i字节索引(不是字符索引)
    • crune(Unicode 字符)

    对比 Java

    Java Go
    char 是 UTF-16 rune 是 UTF-32
    String.length() 不等于字符数 len(s) 是字节数
    遍历字符较绕 range 天然支持 Unicode

    遍历中文,Go 用 range 是“正解”

    m := map[string]int{
        "a": 1,
        "b": 2,
    }
    
    for k, v := range m {
        fmt.Println(k, v)
    }
    

    重要特性

    • map 遍历 无序
    • 每次运行顺序可能不一样
    • Go 刻意这么设计(防止依赖顺序的 bug)

    如果需要有序,需这样处理:

    keys := make([]string, 0, len(m))
    for k := range m {
        keys = append(keys, k)
    }
    sort.Strings(keys)
    
    for _, k := range keys {
        fmt.Println(k, m[k])
    }
    

    在并发中很有用

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

    特点:

    • 一直阻塞
    • 直到 channelclose
    • 非常适合消费协程产生的数据

    问题代码

    arr := []int{1, 2, 3}
    
    for _, v := range arr {
        v = v * 10
    }
    
    fmt.Println(arr) // [1 2 3]
    

    为什么没改?

    • v元素的拷贝
    • 修改 v 不影响原数组

    正确写法

    for i := range arr {
        arr[i] *= 10
    }
    

    场景 建议
    需要修改原数组 用索引 for
    需要精确控制步长 用经典 for
    性能极限场景 手写 for 更可控
    遍历 map 顺序敏感 不适合

    Go 的 range = 更安全、更简洁的 for-each,但要记住:

    1. 值是拷贝;

    2. map 无序;

    3. 字符串按 rune。

    到此这篇关于Go语言中范围range的使用小结的文章就介绍到这了,更多相关Go range内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

    您可能感兴趣的文章:

    • Go语言范围Range的实现示例
    • Go语言范围Range的具体使用

    站内搜索