Golang 的世界里,有最恶名昭彰,实力最强的 4 种数据类型。彼此之间处于竞争关系,但是各有各的特色。由此,各自有各自的生存空间。

它们分别是数组、切片、映射、构建存储(struct)。

数组

// 数组变量声明
var array [5]int

可以看到,数组是一个集合,里面的值都是同一类型,同时,数组在声明的时候就已经限定了元素数量,本例为 5 个元素。有额外的元素想要添加到这个数组怎么办?没办法。这时,如果能有一种可以拓展元素数量的数组,有拓展需求的码农我想一定会很开心。于是,切片出现了……

切片

// 切片的声明
var mySlice []string

通过切片的声明语句,可以看到,切片变量不需要在声明时确定元素数量。也就是说,想给它添加几个元素就可以给它添加几个元素,切片在元素数量上可以更自由。

怎么实现的呢?

切片实际上是一个数组的一个片段[1]

slice1

slice2

slice3

在声明切片时,内存里实际上先创建了一个数组,拿来给这个切片使用。

不是说数组是一种在声明时就确定了元素数量的东西吗?那当它里面的切片拓展超过了容纳它的数组规定的数量时,不就没法实现了吗?

答案是 No

数组和切片的关系可以这么理解:数组是切片的宿主。

切片想要自由扩张,当它的宿主(数组)容量不够时,它就换一个容量更大的数组。切片接着扩张,宿主容量又不够了,换更大的数组。如此往复……

映射(map)

// 映射的声明
var nilMap map[string]bool
nilMap = make(map[string]bool)
nilMap["guitar"] = true
nilMap["dance"] = false

数组、切片都是由索引和值构成。在数组创建时,索引就自动添加好了,从 0 开始递增,每个索引对应一个元素。

key

以前面示例中的映射声明打印出来就是这个样子:

map[dance:false guitar:true]

nilMap["swim"]fmt.Println(nilMap["swim"])false
nilMap["swim"]false
keyfalse
nilMap["dance"]false
nilMap["dance"] = falsenilMap["swim"] = falsenilMap["dance"]falsenilMap["swim"]false

问题来了,我怎么知道一个 `key` 当前赋值了没有?

key
keytruefalse
_, ok1 := nilMap["dance"]
_, ok2 := nilMap["swim"]
fmt.Println(ok1, ok2) // 结果: true false

nilMap["dance"]nilMap["swim"]

你捋清楚了吗?哪里不清楚欢迎留言告诉我!

构建存储(struct)

一个数组只能存储一种类型的值(切片也一样);

keyvalue

我想把不同类型的值存到一个容器里,有办法吗?

构建存储(struct) 应运而生:

package main

import "fmt"

type subscriber struct {
    name string
    rate float64
    active bool
}

func main() {
    var subscriber1 subscriber
    subscriber1.name = "Aman Singh"
    subscriber1.rate = 5.99
    subscriber1.active = true
    fmt.Println(subscriber1)
}

// 结果:{Aman Singh 5.99 true}

typestructkeykeysubscriber
subscribersubscriberintstringboolfloat64
subscriber1subscriber

小结

和打架一个道理,直拳打不着试试摆拳,摆拳不行试试勾拳。

数组不适合考虑下切片,切片不适合考虑下映射(map),映射不适合考虑下构建(struct)。

四大数据类型让你的代码更“能打”

[1]: Jay, McGavren. Head First Go. China Machine Press, 2020.