Go by Example - Arrays, Maps, Slices and Range

Go by Example - Arrays, Maps, Slices and Range

介绍go中的数组, 切片, 映射和range

数组

在 Go 中, 数组是特定长度的元素的编号序列。在典型的 Go 代码中, 切片更为常见, 数组在一些特殊场景下很有用。

arrays.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main

import "fmt"

func main() {
// 这里, 我们创建一个数组a, 它正好容纳5个整数
// 元素的类型和长度都是数组类型的一部分
// 默认情况下, 对于int类型的数组, 其零值是0
var a [5]int
fmt.Println("emp:", a)

// 可以使用array[index]=value语法在索引处设置值, 使用arrary[index]获取值
a[4] = 100
fmt.Println("set:", a)
fmt.Println("get:", a[4])

// 内置的 len 函数返回数组的长度
fmt.Println("len:", len(a))

// 使用此语法在一行中声明和初始化数组
b := [5]int{1, 2, 3, 4, 5}
fmt.Println("dcl:", b)

// 还可以让编译器用 ... 来计算元素的数量
b = [...]int{1, 2, 3, 4, 5}
fmt.Println("dcl:", b)

// 如果使用 : 指定索引, 则中间的元素将为零
b = [...]int{100, 5: 400, 500}
fmt.Println("idx:", b)

// 数组类型是一维的, 但你可以组合类型来构建多维数据结构
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)

// 也可以同时创建和初始化多维数组
twoD = [2][3]int{
{1, 2, 3},
{1, 2, 3},
}
// 请注意, 使用 fmt.Println 打印数组时, 其形式为 [v1 v2 v3 ...]
fmt.Println("2d: ", twoD)
}
log
1
2
3
4
5
6
7
8
9
10
$ go run arrays.go
emp: [0 0 0 0 0]
set: [0 0 0 0 100]
get: 100
len: 5
dcl: [1 2 3 4 5]
dcl: [1 2 3 4 5]
idx: [100 0 0 400 500]
2d: [[0 1 2] [1 2 3]]
2d: [[1 2 3] [1 2 3]]

切片

切片是 Go 中的一种重要数据类型, 它为序列提供了比数组更强大的接口。

slices.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package main

import (
"fmt"
"slices"
)

func main() {
// 与数组不同, 切片仅由其包含的元素 (而不是元素个数) 进行构建
// 未初始化的切片等于 nil, 长度为 0
var s []string
fmt.Println("uninit:", s, s == nil, len(s) == 0)

// 若要创建长度为非零的空切片, 请使用内置的make
// 在这里, 我们制作一个长度为3 (最初为零值) 的字符串切片
// 默认情况下, 新切片的容量等于其长度;
// 如果我们知道切片将提前增长, 那么可以显式地传递一个容量作为要生成的附加参数
s = make([]string, 3)
fmt.Println("emp:", s, "len:", len(s), "cap:", cap(s))

// 可以像使用数组一样设置和获取值
s[0] = "a"
s[1] = "b"
s[2] = "c"
fmt.Println("set:", s)
fmt.Println("get:", s[2])

// len 按预期返回切片的长度
fmt.Println("len:", len(s))

// 除了这些基本操作外, 片段还支持其他一些使其比数组更丰富的操作
// 其中一个是内置的 append, 它可以返回一个包含一个或多个新值的片段
// 请注意, 需要接受 append 的返回值, 因为我们可能会得到一个新的片段值
s = append(s, "d")
s = append(s, "e", "f")
fmt.Println("apd:", s)

// 切片也可以复制。创建一个与 s 长度相同的空片段 c, 并从 s 复制到 c 中
c := make([]string, len(s))
copy(c, s)
fmt.Println("cpy:", c)

// 切片支持使用 slice[low:high] 语法的 "切片 "操作符
// 例如, 这样可以得到 s[2]、s[3] 和 s[4] 这几个元素的切片
l := s[2:5]
fmt.Println("sl1:", l)

//这种切分方法分割到 (但不包括) s[5]
l = s[:5]
fmt.Println("sl2:", l)

// 这是从 (包括) s[2]开始的切片
l = s[2:]
fmt.Println("sl3:", l)

//也可以在一行中声明和初始化一个切片变量
t := []string{"g", "h", "i"}
fmt.Println("dcl:", t)

// slices 包包含大量有用的切片实用功能
t2 := []string{"g", "h", "i"}
if slices.Equal(t, t2) {
fmt.Println("t == t2")
}

// 切片可以组成多维数据结构。与多维数组不同, 内部切片的长度可以变化
twoD := make([][]int, 3)
for i := 0; i < 3; i++ {
innerLen := i + 1
twoD[i] = make([]int, innerLen)
for j := 0; j < innerLen; j++ {
twoD[i][j] = i + j
}
}
fmt.Println("2d: ", twoD)
// 虽然切片与数组的类型不同, 但fmt.Println对它们的渲染方式类似
}
log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ go run slices.go
uninit: [] true true
emp: [ ] len: 3 cap: 3
set: [a b c]
get: c
len: 3
apd: [a b c d e f]
cpy: [a b c d e f]
sl1: [c d e]
sl2: [a b c d e]
sl3: [c d e f]
dcl: [g h i]
t == t2
2d: [[0] [1 2] [2 3 4]]

请查看 Go 团队的这篇精彩博文, 了解有关 Go 中切片的设计和实现的更多详情

映射

映射是 Go 内置的关联数据类型 (在其他语言中有时称为散列或 dicts) 。

maps.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package main

import (
"fmt"
"maps"
)

func main() {
// 要创建一个空映射, 请使用内置的make: make(map[key-type]value-type)
m := make(map[string]int)

// 使用 name[key] = value 语法设置键/值对
m["k1"] = 7
m["k2"] = 13

// 使用例如fmt.Println打印一个映射将显示其所有键/值对
fmt.Println("map:", m)

// 通过name[key]获取一个键的值
v1 := m["k1"]
fmt.Println("v1:", v1)

// 如果键不存在, 则返回值类型的零值
v3 := m["k3"]
fmt.Println("v3:", v3)

// 内置的 len 在映射上调用时会返回键/值对的数量
fmt.Println("len:", len(m))

// 内置的delete从映射中删除键/值对
delete(m, "k2")
fmt.Println("map:", m)

// 要从映射中删除所有键/值对, 使用clear内置语句
clear(m)
fmt.Println("map:", m)

// 从映射中获取值时, 可选的第二个返回值表示该键是否存在于映射中
// 这可以用来区分丢失的键和值为 0 或""的键
// 在这里, 我们不需要值本身, 所以用空白标识符 _ 忽略了它
_, prs := m["k2"]
fmt.Println("prs:", prs)

// 也可以使用此语法在同一行中声明和初始化新映射
n := map[string]int{"foo": 1, "bar": 2}
fmt.Println("map:", n)

// maps 包包含许多映射相关有用的工具函数
n2 := map[string]int{"foo": 1, "bar": 2}
if maps.Equal(n, n2) {
fmt.Println("n == n2")
}
// 请注意, 在使用 fmt.Println 打印时, 映射会以 map[k:v k:v] 的形式显示
}
log
1
2
3
4
5
6
7
8
9
10
$ go run maps.go
map: map[k1:7 k2:13]
v1: 7
v3: 0
len: 2
map: map[k1:7]
map: map[]
prs: false
map: map[bar:2 foo:1]
n == n2

range

range 可以遍历各种数据结构中的元素。让我们看看如何将 range 用于我们已经学过的一些数据结构。

range.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import "fmt"

func main() {
// 在这里,我们使用range对切片中的数字求和。数组也可以这样
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)

// 数组和片段上的 range 可以为每个条目提供索引和值
// 上面我们不需要索引,所以用空白标识符 _ 忽略了它。但有时我们确实需要索引
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}

// map上的range在键/值对上迭代
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}

// range 也可以只遍历映射的键
for k := range kvs {
fmt.Println("key:", k)
}

// 字符串范围遍历 Unicode 码点
// 第一个值是符文的起始字节索引,第二个值是符文本身
for i, c := range "go" {
fmt.Println(i, c)
}
}
log
1
2
3
4
5
6
7
8
9
$ go run range.go
sum: 9
index: 1
a -> apple
b -> banana
key: a
key: b
0 103
1 111

关于遍历 Unicode 码点的更多详情,请参阅字符串和符号

参考链接

作者

孤独小狼

发布于

2024-01-20

更新于

2024-01-20

许可协议

评论