天天看点

Golang入门专题-Range迭代器

Range

range遍历各种数据结构中的元素,本质上是Go内置的迭代器。

package main

import "fmt"

func main() {
    // 迭代一个slice,对元素求和
    nums := []int{2, 3, 4}
    sum := 0
    // range函数有返回值,如果是迭代slice,则返回index和value
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)

    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }

    // 迭代map,则返回key和value
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }

    // 仅迭代map的keys,返回key
    for k := range kvs {
        fmt.Println("key:", k)
    }
  
    // 迭代字符串,返回char的index和char对应的字符编码
    // g的ascii码为103,o的ascii码为111
    for i, c := range "go" {
        fmt.Println(i, c)
        // 还原为字符串
        fmt.Println(i, string(c))
    }
}           

上面程序的输出:

$ go run range.go
sum: 9
index: 1
a -> apple
b -> banana
key: a
key: b
0 103
1 111           

遍历通道

对于通道来说,range 会一直阻塞直到通道关闭,每次迭代返回通道中的下一个值。

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

上面程序的输出:

1
2
3           

忽略返回值

有时候我们只需要遍历数组、切片、字符串、映射或通道中的值,而不需要用到下标或键。这时候可以使用空白标识符 _ 来忽略下标或键。

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

m := map[string]int{"foo": 1, "bar": 2, "baz": 3}
for k := range m {
    fmt.Println(k)
}

ch := make(chan int)
go func() {
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)
}()
for range ch {
    fmt.Println("received from channel")
}           

上面程序的输出:

1

2

3

foo

bar

baz

received from channel

received from channel

received from channel

在这个例子中,我们使用空白标识符 _ 来忽略了数组、切片、字符串、映射和通道中的下标或键。

跳出迭代

在循环中,可以使用 break 关键字来跳出循环,或使用 continue 关键字来跳过当前迭代。

s := []int{1, 2, 3, 4, 5}
for _, v := range s {
    if v == 3 {
        break
    }
    fmt.Println(v)
}

m := map[string]int{"foo": 1, "bar": 2, "baz": 3}
for k, v := range m {
    if k == "bar" {
        continue
    }
    fmt.Println(k, v)
}           

上面程序的输出:

1

2

foo 1

baz 3

在这个例子中,我们使用 break 来在数组、切片、字符串、映射和通道中跳出循环,或使用 continue 来跳过当前迭代。

多维数组

如果需要遍历多维数组或切片,可以使用多个 range。每个 range 对应一个维度。

arr := [2][3]int{{1, 2, 3}, {4, 5, 6}}
for _, row := range arr {
    for _, v := range row {
        fmt.Println(v)
    }
}

s := [][]int{{1, 2}, {3, 4, 5}, {6, 7, 8, 9}}
for _, row := range s {
    for _, v := range row {
        fmt.Println(v)
    }
}           

每日一Tip

需要注意的是,对于映射来说,range 的迭代顺序是随机的。如果需要按照特定顺序迭代映射的键或值,可以将其存储到一个切片中,然后对切片进行排序或者自定义排序函数。