[译]GO BOOTCAMP 第四章:集合类型
  热度 °
Go
集合类型包含Array
,Slice
,Range
,Map
等议题的分析和讨论。
Array-数组
[n]T
表示为一个n个数据类型为T的数组.
1 | var a [10]int |
表示声明一个包含10个整数的数组变量a.
数组的长度是它类型的一部分,所以数组在初始化之后就不能再更改大小,不过Go
提供了一种方便的方式来使用数组.
1 | package main |
可以在声明数组的同时初始化数组
1 | package main |
在初始化数组时,也可以使用三个省略号来代替具体指明数组的大小1
2
3
4
5
6
7
8package main
import "fmt"
func main() {
a := [...]string{"hello", "world!"}
fmt.Printf("%q", a)
}
打印数组
之前使用fmt
包的Printf
函数和使用%q
参数来打印每一个引用的元素,现在我们使用Println
或%s
参数来打印,将会得到不同的输出1
2
3
4
5
6
7
8
9
10
11
12
13package main
import "fmt"
func main() {
a := [2]string{"hello", "world!"}
fmt.Println(a)
// [hello world!]
fmt.Printf("%s\n", a)
// [hello world!]
fmt.Printf("%q\n", a)
// ["hello" "world!"]
}
多维数组
创建多维数组1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package main
import "fmt"
func main() {
var a [2][3]string
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
a[i][j] = fmt.Sprintf("row %d - column %d", i+1, j+1)
}
}
fmt.Printf("%q", a)
// [["row 1 - column 1" "row 1 - column 2" "row 1 - column 3"]
// ["row 2 - column 1" "row 2 - column 2" "row 2 - column 3"]]
}
如果试图通过索引取访问或修改某个不存在的数组元素,在编译时会报错不通过1
2
3
4
5
6package main
func main() {
var a [2]string
a[3] = "Hello"
}
当试图编译上述代码,会得到如下报错信息:
1 | Invalid array index 3 (out of bounds for 2-element array) |
这是因为长度为2的数组下标索引为0或1,试图获取索引3是数组越界,所以报上述error.
因为在使用数组之初,通常不能确定数组的长度, 所以golang
提供了另一种常用的数据类型Slices
.
Slices-切片
Slices
的底层依然是一个数组,不过封装过后的它更方便用于处理数据,除了多维数组以外,在Go
编程中大多数情况使用slices
而不是简单的使用数组.
Slices
是对底层数组的一个引用,如果将一个Slices
直接赋值给另一个Slices
,那这两个Slices
指向的是同一个数组. 将Slices
通过函数形参传入,所做的修改会直接影响slices
中的元素, 效果跟传入指针数组类似.
Slices
包含一个指向数组的指针和这个数组的长度值。Slices
可以被重新定义长度,因为它们仅仅是数组的一个顶层封装.
[]T
表示为一个数据类型为T
的切片.
1 | package main |
可以基于现有的切片创建新的切片,表达式:1
s[lo:hi]
上述表达式表示创建了一个新的切片,包含的数据为现有切片数据索引lo
到索引hi
的范围,因此1
s[lo:lo]
表示一个空的切片.1
s[lo:lo+1]
表示有一个元素的切片.
lo
和hi
是表示索引的整型整数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20package main
import "fmt"
func main() {
mySlice := []int{2, 3, 5, 7, 11, 13}
fmt.Println(mySlice)
// [2 3 5 7 11 13]
fmt.Println(mySlice[1:4])
// [3 5 7]
// missing low index implies 0
fmt.Println(mySlice[:3])
// [2 3 5]
// missing high index implies len(s)
fmt.Println(mySlice[4:])
// [11 13]
}
声明 slices
除了可以通过在声明右边赋值来初始化一个Slices
外, 还可以使用关键字make
,方便用于创建一个指定初始长度的空Slices
.1
2
3
4
5
6
7
8
9
10
11
12package main
import "fmt"
func main() {
cities := make([]string, 3)
cities[0] = "Santa Monica"
cities[1] = "Venice"
cities[2] = "Los Angeles"
fmt.Printf("%q", cities)
// ["Santa Monica" "Venice" "Los Angeles"]
}
通过make
关键字表示创建一个空数组,并返回这向这个数组的指针。
给slice 追加数据 1
2cities := []string{}
cities[0] = "Santa Monica"
上述代码将报出一个运行时错误,因为slice
是一个数组的引用,初始化一个长度为零的空数组时,不能直接通过slice
给其引用的长度为0的空数组赋值。但是可以通过关键字append
追加来做这件事情.1
2
3
4
5
6
7
8
9
10package main
import "fmt"
func main() {
cities := []string{}
cities = append(cities, "San Diego")
fmt.Println(cities)
// [San Diego]
}
也可以给slice
一次append
多个值1
2
3
4
5
6
7
8
9
10package main
import "fmt"
func main() {
cities := []string{}
cities = append(cities, "San Diego", "Mountain View")
fmt.Printf("%q", cities)
// ["San Diego" "Mountain View"]
}
也可以用省略号...
来append
多个数据1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
func main() {
cities := []string{"San Diego", "Mountain View"}
otherCities := []string{"Santa Monica", "Venice"}
cities = append(cities, otherCities...)
fmt.Printf("%q", cities)
// ["San Diego" "Mountain View" "Santa Monica" "Venice"]
}
省略号...
是Go
语言的一个内建特征,意味着这个元素是一个集合. 如果不用省略号不能给一个[]string
直接append
一个[]string
, 只能append
strings. 如果用了省略号则可以append
,当然append
的slice
数据类型需要和被append
的数据类型相同,比如就不能将[]int
直接append
到一个[]string
的slice
上。
长度
使用关键字len
可以得到一个slice
的长度.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package main
import "fmt"
func main() {
cities := []string{
"Santa Monica",
"San Diego",
"San Francisco",
}
fmt.Println(len(cities))
// 3
countries := make([]string, 42)
fmt.Println(len(countries))
// 42
}
Nil slices
空slice
的零值为nil
, 一个nil
的slice
长度和容量都为01
2
3
4
5
6
7
8
9
10
11
12
13package main
import "fmt"
func main() {
var z []int
fmt.Println(z, len(z), cap(z))
// [] 0 0
if z == nil {
fmt.Println("nil!")
}
// nil!
}
参考更多
- Go slices, usage and internals
- Effective Go - slices
- Append function documentation
- Slice tricks
- Effective Go - slices
- Effective Go - two-dimensional slices
- Go by example - slices
循环slice-Range
循环迭代slice
或map
, 用关键字range
,用range
关键字可以很方便迭代一个数据结构的所有元素.1
2
3
4
5
6
7
8
9
10
11package main
import "fmt"
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
func main() {
for i, v := range pow {
fmt.Printf("2**%d = %d\n", i, v)
}
}
result:1
2
3
4
5
6
7
82**0 = 1
2**1 = 2
2**2 = 4
2**3 = 8
2**4 = 16
2**5 = 32
2**6 = 64
2**7 = 128
如果不需要index
则可以直接_
丢弃即可,如果只需要index
,则可以不用管返回的value
参数1
2
3
4
5
6
7
8
9
10
11
12
13package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
}
for _, value := range pow {
fmt.Printf("%d\n", value)
}
}
Break & continue
可以使用break
去stop一次迭代1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
pow[i] = 1 << uint(i)
if pow[i] >= 16 {
break
}
}
fmt.Println(pow)
// [1 2 4 8 16 0 0 0 0 0]
}
也可以使用continue
关键字来跳过一次迭代1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package main
import "fmt"
func main() {
pow := make([]int, 10)
for i := range pow {
if i%2 == 0 {
continue
}
pow[i] = 1 << uint(i)
}
fmt.Println(pow)
// [0 2 0 8 0 32 0 128 0 512]
}
Range and maps range
关键字同样也可以用于迭代map
, 但是返回的第一个参数值不是一个递增的索引而是map
的key
值.1
2
3
4
5
6
7
8
9
10
11
12
13
14package main
import "fmt"
func main() {
cities := map[string]int{
"New York": 8336697,
"Los Angeles": 3857799,
"Chicago": 2714856,
}
for key, value := range cities {
fmt.Printf("%s has %d inhabitants\n", key, value)
}
}
the result:1
2
3New York has 8336697 inhabitants
Los Angeles has 3857799 inhabitants
Chicago has 2714856 inhabitants
练习题
给定一个名字列表的slices
, 要求将相同长度的名字,放到同一个slice
中;输出的结果如下:1
2
3[[] [] [Ava Mia] [Evan Neil Adam Matt Emma] [Emily Chloe]
[Martin Olivia Sophia Alexis] [Katrina Madison Abigail Addison Natalie]
[Isabella Samantha] [Elizabeth]]
习题解答 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main
import "fmt"
var names = []string{"Katrina", "Evan", "Neil", "Adam", "Martin", "Matt",
"Emma", "Isabella", "Emily", "Madison",
"Ava", "Olivia", "Sophia", "Abigail",
"Elizabeth", "Chloe", "Samantha",
"Addison", "Natalie", "Mia", "Alexis"}
func main() {
var maxLen int
for _, name := range names {
if l := len(name); l > maxLen {
maxLen = l
}
}
output := make([][]string, maxLen)
for _, name := range names {
output[len(name)-1] = append(output[len(name)-1], name)
}
fmt.Printf("%v", output)
}
- 为了避免插入越界, 需要申请一个足够大的
slice
,但是并不需要申请一个过于大的slice
,这是为什么我们首选获得最长的name
的length
, 并且以这个length
作为输出slice
的长度.因为slice
是从索引0开始的,所以插入时需要获取name
的长度减一。
Maps
Go
语言中的Map
数据结构类似于其它语言中的字典
或hashes
。
一个Map
就是一个key-values
值对,下面将演员的名字作为Map
的key
, 年龄作为Map
的values
.1
2
3
4
5
6
7
8
9
10
11
12
13
14package main
import "fmt"
func main() {
celebs := map[string]int{
"Nicolas Cage": 50,
"Selena Gomez": 21,
"Jude Law": 41,
"Scarlett Johansson": 29,
}
fmt.Printf("%#v", celebs)
}
the result:1
2map[string]int{"Nicolas Cage":50, "Selena Gomez":21, "Jude Law":41,
"Scarlett Johansson":29}Map
在使用前,必需先用make
关键字初始化,否在不能使用,也不能添加值.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m map[string]Vertex
func main() {
m = make(map[string]Vertex)
m["Bell Labs"] = Vertex{40.68433, -74.39967}
fmt.Println(m["Bell Labs"])
}
在使用Map
,如果顶层类型只是一个类型名,那么在初始化赋值时可以省略它1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
// same as "Bell Labs": Vertex{40.68433, -74.39967}
"Google": {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}
Maps基本操作
插入或更新map
中的元素1
m[key]=elem
获取元素1
elem=m[key]
删除一个元素1
delete(m,key)
测试是否存在某个值1
elem, ok = m[key]
如果m
中存在key
, 则ok
返回true
,否则返回false
表示不存在,并且elem
是map
相应数据类型的零值.
参考更多
练习题
实现单词统计1
2
3
4
5
6
7
8
9
10
11
12
13package main
import (
"code.google.com/p/go-tour/wc"
)
func WordCount(s string) map[string]int {
return map[string]int{"x": 1}
}
func main() {
wc.Test(WordCount)
}
应该返回一个map
,包含每个单词的长度值和单词的映像关系.
解决方案 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package main
import (
"code.google.com/p/go-tour/wc"
"strings"
)
func WordCount(s string) map[string]int {
words := strings.Fields(s)
count := map[string]int{}
for _, word := range words {
count[word]++
}
return count
}
func main() {
wc.Test(WordCount)
}
作者署名:朴实的一线攻城狮
本文标题:[译]GO BOOTCAMP 第四章:集合类型
本文出处:http://researchlab.github.io/2016/01/19/go-collection-types/
版权声明:本文由Lee Hong创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处, 否则保留追究法律责任的权利。