综述
- 数组和结构体是聚合类型,其值由许多元素或成员字段的值组成,且都有固定内存大小的数据结构
- 数组是由同构的元素组成
- 结构体是由异构的元素组成
- slice 和 map 则是动态的数据结构
4.1 数组
定义
- 数组是一个由固定长度的特定类型元素组成的序列
- 数组可有0个或多个元素
- 数组元素可通过索引下标来访问,0<= 索引下标 <= 数组长度 - 1
- len()函数:返回数组元素个数,即长度
- 数组长度必须是常量表达式,因为需要在编译阶段确定
- 只有等两个数组的所有元素都是相等的时候数组才相等
- 长度不一样的数组属于不同类型的数组,不能进行比较,会出现编译错误
- GO不会隐式地将数组作为引用或指针对象传入被调用的函数中
初始化
- 数组的每个元素都被初始化为元素类型对应的零值
var a [3] int
var q [3] int = [3]int{1,2}
a := [...]int{1,2,3} //...表示数组长度由初始化值的个数来计算
r := [...]int{99:-1} //表示定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其他元素初始化为0
4.2 Slice
定义
- slice(切片)代表长度可以变化的序列,序列中每个元素都有相同的类型
- 格式:[]T —— T表示slice中元素的类型
- slice构成:指针、长度和容量
- 指针:指向第一个slice对应的底层数组元素的地址(注:slice的第一个元素不一定是数组的第一个元素)
- 长度:对应slice中元的数目,不可超过容量,len()函数
- 容量:一般是从slice的开始位置到底层数据的结尾位置,cap()函数
- 多个slice之间可以共享底层的数据,并且引用的数组部分区间可能重叠
切片操作
-
s[i:j] (0 <= i <= j <= cap(s)),用于创建一个新的slice(返回原序列的子序列),i 的默认值是0,j 的默认值是 len(s)
image.png - 切片操作超出 cap(s)的上限将导致一个panic异常,但超出len(s)则意味着扩展了slice,则新的slice的长度会变大
其他
- 向函数传递slice将寻在函数内部修改底层数组的元素
- slice之间不能比较
- bytes.Equal()函数判断两个字节型 slice 是否相等([]byte)
- slice 可以和 nil 比较:一个零值的 slice 等于 nil,此时slice并没有底层数组,长度和容量为 0
- []int(nil) 类型转换表达式生成一个而对应类型的slice的nil值
var s []int // len(s) == 0, s == nil
s = nil // len(s) == 0, s == nil
s = []int(nil) // len(s) == 0, s == nil
s = []int{} // len(s) == 0, s != nil
- 判断 slice 是否为空,使用 len(s) == 0 判断
- 内置 make 函数创建一个指定元素类型、长度和容量(可省略,容量 = 长度)的 slice
make([]T, len)
make([]T, len, cap)
函数
append函数
- 定义:append函数用于向slice追加元素
var runes []rune
for _, r := range "hello,world" {
runes = append(runes, r)
}
fmt.Printf("%q\n", runes)
copy函数
- 定义:将一个slice复制为另一个类型相同的slice
- 参数:
- 第一个参数:要复制的目标slice
- 第二个参数:源slice
- 返回值:返回成功复制的元素个数
4.3 Map
定义及其他
- 哈希表是一个无序的key/value对的集合,其中所有key都不同,可通过给定的key在常数时间复杂度内检索、更新或删除对应的value
- 一个 map 是一个哈希表的引用
- map类型格式:map[K]V,K— key,V—value
- map 中的key是相同类型(注:必须是可以支持 == 的数据类型,但最好不要用 float类型),value也是相同的类型,两者类型可不同
- 内置的make函数可以创建一个map
ages := make(map[string]int)
// map字面值的语法创建map
ages := map[string]int{
"alice" : 31,
"charlie": 32,
}
//创建空的map的表达式
map[string]int{}
- map 中的元素可通过key对应的下标语法来访问
- delete()函数可以删除元素
- 重点:不能对map的元素进行取地址操作,因为map中的元素并不是一个变量
- 每次遍历map得到的结果都是随机的
- map 类型的零值是 nil
- 向一个 nil 值的map存入元素将导致一个panic异常,存数前必须创建map
map下标语法
age, ok := ages["bob"]
if !ok {}
if age, ok := ages["bob"]; !ok {}
返回两个值,第二个是布尔值报告元素是否真的存在
- map之间不能进行相等比较,但可以和 nil 比较
slice类型或不可比较的类型作为 map 的key
- fmt.Sprintf("%q", slice) 函数将字符串列表(slice)或不可比较的类型转换为一个字符串以用于map的key
map的value类型
- value的类型也可以是一个聚合类型,如:map或slice
4.4 结构体
定义
- 结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee
- 每个值称为结构体的成员
- 结构体变量的成员可以通过点操作符访问,可进行赋值、取地址操作
- 一个结构体可同时包含可导出(首字母大写)或不可导出的成员
- 一个命名为S的结构体类型将不能再包含S类型的成员(一个聚合的值不能包含它自身),但可以包含 *S 指针类型的成员
- 二叉树排序
package main
import (
"fmt"
)
type tree struct {
value int
left, right *tree
}
func Sort(values []int) {
var root *tree
for _, v := range values {
root = add(root, v)
}
appendValues(values[:0], root)
}
func appendValues(values []int, t *tree) []int {
if t != nil {
values = appendValues(values, t.left)
values = append(values, t.value)
values = appendValues(values, t.right)
}
return values
}
func add(t *tree, value int) *tree {
if t == nil {
t = new(tree)
t.value = value
return t
}
if value < t.value {
t.left = add(t.left, value)
} else {
t.right = add(t.right, value)
}
return t
}
func main() {
values := []int{11, 2, 5, 30, 100, 10, 1, 70}
Sort(values)
fmt.Println("%d\n", values)
}
结构体字面值
- 结构体字面值可以指定每个成员的值:
第一种写法
type Point struct {X, Y int}
p := Point{1, 2}
- 此种写法要求以结构体成员定义的顺序为每一个结构体成员指定一个字面值
- 此种写法一般只在定义结构体的包内部或在较小的结构体中使用
第二种写法(常用)
anim := gif.GIF{LoopCount: nframes}
- 以成员名字和相应的值来初始化,可包含部分或全部成员
- 若成员被忽略则默认使用零值
结构体可以作为函数的参数和返回值
- 要在函数内部修改结构体成员的话,必须传入指针
- 创建并初始化一个结构体变量并返回结构体的地址:
pp := &Point{1, 2}
//等价于下面的语句
pp := new(Point)
*pp = Point{1, 2}
结构体比较
- 若结构体的全部成员是可以比较的话,那两个结构体也可以使用 == 或 != 进行比较
- 可比较的结构体类型可用于map的key类型
结构体嵌入和匿名成员
匿名成员
- 只声明一个成员对应的数据类型而不指名成员的名字
- 匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针
type Point struct {
X, Y int
}
type Circle struct {
Point
Radius int
}
type Wheel struct {
Circle
Spokes int
}
- 直接访问叶子属性
var w Wheel
w.X = 8
w.Y = 8
w.Radius = 5
w.Spokes = 20
- 结构体字面值并没有简短表示匿名成员的语法
- 结构体字面值必须遵循形状类型声明时的结构
4.5 JSON
定义
- JSON:JavaScript对象表示法是一种用于发送和接收结构化信息的标准协议
- 标准库:encoding/json
- 基本JSON类型:
- 数字、布尔值、字符串
- 格式:
- 一个JSON数组是一个有序的值序列
- 一个JSON数组可以用于编码GO语言的数组和slice
array
["gold", "silver", "bronze"]
- 一个JSON对象是一个字符串到值的映射
object
{
"year": 2019,
"event": "archery",
"medals": ["gold", "silver", "bronze"]
}
- JSON的对象类型可以用于编码GO语言的map类型(key类型是字符串)和结构体
编组(marshaling)
- 将结构体slice转为JSON的过程叫编组
- 函数:json.Marshal()
解码(unmarshaling)
- 函数:json.Unmarshal()
文本和HTML模板
模板包
- text/template、html/template 等模板包提供了一个将变量值填充到一个文本或者HTML格式的模板的机制
- 一个模板是一个字符串或一个文件,里面包含了一个或多个由双花括号包含的 {{action}} 对象
- 每个 action 都包含了一个用模板语言书写的表达式