Go In Action 读书笔记 一

架构流程图

关键字

var

变量使用var声明, 如果变量不是定义在任何一个函数作用域内, 这个变量就是包级变量. > Go语言中, 所有变量都被初始化为其零值. 对于数值类型, 其零值是0; 对于字符串类型, 其零值是空字符串””; 对于布尔类型, 其零值是false. 对于引用类型来说, 底层数据结构会被初始化对应的零值. 但是被生命被起零值的引用类型的变量, 会返回nil作为其值.

const

定义常量

interface

声明接口

func

声明函数

defer

安排后面的函数调用在当前函数返回时才执行.

1
2
3
4
5
file, err = os.open("filePath")
if err != nil
    return
defer file.close()
# more file operation

go

启动后面的函数作为goroutine, 如下面启动匿名函数作为goroutine

1
go func(){}()

import

导入包, 让使用者可以访问其中的标识符, 如类型, 函数, 常量和接口. 编译器查找包时会从GOROOTGOPATH环境变量引用的位置去查找. 如果引用的包名前使用下划线_, 表明不直接使用包里的标识符, 只是调用其init函数执行初始化操作

1
2
3
4
5
6
import (
    "log"
    "fmt"
    _ package/hasNoPublicIdentifier
    package/hasPublicIdentifier
)

range

用于迭代数组, 字符串, 切片, 映射和通道

迭代通道时, 如果通道中没有数据时会阻塞; 有数据写入时会触发执行后面的代码. 如果通道关闭, 迭代退出.

type

声明结构类型

struct

结构类型

语法

标识符

小写字母开头的标识符不会暴露, 只会暴露大写字母开头的标识符

main包

程序的入口可以在main.go文件里找到. 每个可执行的Go程序有2个特征: 有main函数, 程序的第01行包名为main

1
2
3
4
package main
import ()
func init() {}
func main() {}

init函数

init函数总是在main函数调用之前被调用. 常见import中使用下划线_引入没有暴露任何标识符的包, 调用其init函数

所有处于同一个文件夹下的代码文件, 必须使用同样的包名

一个函数多个返回值

1
value, err := RetriveValue()

简化声明变量运算符 (:=)

声明变量同时赋值, 根据后面的类型确定变量的类型.

1
val := make(map[string]string)

函数间的变量传递

都是值传递

数组, 切片和映射

数据

长度固定的数据类型. 在内存中的占用是连续的.

声明和初始化

1
2
3
4
var array [5]int
array :=[5]int{1,2,3,4,5}
array :=[...]int{1,2,3,4,5}
array :=[5]int{1: 1, 2: 2} #array[0]为0

使用

使用索引访问

1
2
array[2] #2
array[2]=3 #修改
指针数组
1
2
3
4
array := [5]*int{0: new(int), 1: new(int)}
//复制
*array[0]=1 
*array[1]=2
多维数组
1
array :=[4][2]int{0: {1, 2}, 1: {3,4}}

函数间传递数组

值传递

1
2
3
4
5
6
//声明一个需要8MB的数组
var array [1e6]int
//传递数组
foo(array)
//接受一个100w个整形值的数组
func foo(array [1e6]int){}

使用指针传递

1
2
3
4
5
6
//声明一个需要8MB的数组
var array [1e6]int
//传递数组
foo(&array)

func foo(array *[1e6]int){}

切片

一种数据结构, 便于使用和管理数据集合. 是围绕动态数组的概念创建的, 就是可变(增长或缩小)数组. 切面的底层内存也是在连续快中分配的, 也能获得索引,迭代. 切片的动态增长是通过内置函数append实现的.

切片是一个很小的对象, 对底层数组进行了抽象, 并提供操作方法. 包含三个字段: 指向底层数组的指针, 元素个数(长度)和容量.

创建和初始化

1
2
3
4
5
//指定长度, 长度等于容量
slice := make([]string, 5)
//指定长度和容量, 只能访问3个, 其余2个通过后期操作合并
slice := make([]string, 3, 5)
slice := []int{1,2,3,4,5}
nil和空切片

nil切片

1
var slice []int

空切片, 长度为0, 容量为0

1
2
slice := make([]int, 0)
slice := []int{}
使用切片
使用一个索引访问数组元素
1
2
slice[1]=10
slice[1] #10

使用切片创建切片, 新旧切片共享底层数组

使用两个索引创建新的索引(共用底层数组)
1
2
3
4
//底层数组长度为5
//长度为2 = 3 - 1
//容量为4 = 5 - 1
newSlice := slice[1:3]

增长

使用内置的append方法追加, 返回一个新的切片(新的底层数组, 数组指针改变, 长度改变, 容器可能改变)

1
newSlice := appen(slice[1,3], 6)
三个索引

使用三个索引, 第一个索引表示起始位置, 第二个元素表示起始索引加上希望包括的元素个数 2 + 1 = 3. 第三个索引是起始索引加上容量 2 + 2 = 4.

1
2
3
4
//从第3个元素开始截取
//长度为1 = 3 - 2
//容量为2 = 4 - 2
newSlice := slice[2,3,4]

迭代切片

使用关键字range

1
2
3
4
5
6
7
8
9
slice := []int{1,2,3,4,5}
for idx, val := range slice {
    fmt.Printf("Index: %d Values: %d\n", idx, val)
}

//返回长度
len(slice)
//返回容量
cap(slice)

多维切片

函数间的切片传递

映射

存储一系列无序键值对的数据结构, 可以基于键快速检索.

是一个集合, 可以使用类似数组或切片的方式迭代数组. 但是是无序的, 无法预测键值对被返回的顺序.

无序的原因是映射使用了散列表.

实现方式

桶的数据结构: 两个数组. 一个存储散列键的高八位值, 用来做桶定位. 另一个是字节数组, 用于存储键值对. 先一次存储所有的键, 再一次存储所有的值.

将键通过散列函数计算出散列值, 然后通过散列值的高八位定位出桶, 然后在桶的数组里进行存储, 删除或者查找.

键可以是任何类型, 只要这个值可以使用==运算符做比较. 切片, 函数以及包含切片的机构类型由于具有引用语义, 不能作为映射的键.

创建和初始化

1
2
3
4
5
6
dict := make(map[string]int)
dict := map[string]string{"red":"#da1337", "orange":"#e95a22"}
//空映射
dict := map[string]int{}
//使用切片作为键
dict := map[[]string]int{} #编译错误 invalid map key type []string

使用

空映射

1
2
3
#声明一个空映射
colors := map[string]string[]{}
colors["red"] = "#da1337"

nil映射

1
2
3
//声明为nil映射
var colors map[string]string
colors["red"] = "#da1337"  //运行时出错 assignment to entry in nil map

判断是否存在键. 如果不存在exist为false, value为零值. 如果存在exist为true, value为对应的值.

1
2
3
4
value, exists = colors["blue"]
if exists {
    ...
}

遍历映射

1
2
3
for key, value range colors {
    fmt.printf("Key: %s, Value %s\n", key, value)
}

删除键值对

1
delete(colors, "red")

Comments

comments powered by Disqus