第四章 复合数据类型

综述

  • 数组和结构体是聚合类型,其值由许多元素或成员字段的值组成,且都有固定内存大小的数据结构
  • 数组是由同构的元素组成
  • 结构体是由异构的元素组成
  • 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 都包含了一个用模板语言书写的表达式
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容