Go In Action 读书笔记 一
关键字
var
变量使用var
声明, 如果变量不是定义在任何一个函数作用域内, 这个变量就是包级变量.
Go语言中, 所有变量都被初始化为其零值. 对于数值类型, 其零值是0; 对于字符串类型, 其零值是空字符串""; 对于布尔类型, 其零值是false. 对于引用类型来说, 底层数据结构会被初始化对应的零值. 但是被生命被起零值的引用类型的变量, 会返回nil作为其值.
const
定义常量
interface
声明接口
func
声明函数
defer
安排后面的函数调用在当前函数返回时才执行.
file, err = os.open("filePath")
if err != nil
return
defer file.close()
# more file operation
go
启动后面的函数作为goroutine
, 如下面启动匿名函数作为goroutine
go func(){}()
import
导入包, 让使用者可以访问其中的标识符, 如类型, 函数, 常量和接口.
编译器查找包时会从GOROOT
和GOPATH
环境变量引用的位置去查找.
如果引用的包名前使用下划线_
, 表明不直接使用包里的标识符, 只是调用其init
函数执行初始化操作
import (
"log"
"fmt"
_ package/hasNoPublicIdentifier
package/hasPublicIdentifier
)
range
用于迭代数组, 字符串, 切片, 映射和通道
迭代通道时, 如果通道中没有数据时会阻塞; 有数据写入时会触发执行后面的代码. 如果通道关闭, 迭代退出.
type
声明结构类型
struct
结构类型
语法
标识符
小写字母开头的标识符不会暴露, 只会暴露大写字母开头的标识符
main包
程序的入口可以在main.go文件里找到. 每个可执行的Go程序有2个特征: 有main
函数, 程序的第01行包名为main
package main
import ()
func init() {}
func main() {}
init函数
init
函数总是在main
函数调用之前被调用. 常见import
中使用下划线_
引入没有暴露任何标识符的包, 调用其init
函数
包
所有处于同一个文件夹下的代码文件, 必须使用同样的包名
一个函数多个返回值
value, err := RetriveValue()
简化声明变量运算符 (:=)
声明变量同时赋值, 根据后面的类型确定变量的类型.
val := make(map[string]string)
函数间的变量传递
都是值传递
数组, 切片和映射
数据
长度固定的数据类型. 在内存中的占用是连续的.
声明和初始化
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
使用
使用索引访问
array[2] #2
array[2]=3 #修改
指针数组
array := [5]*int{0: new(int), 1: new(int)}
//复制
*array[0]=1
*array[1]=2
多维数组
array :=[4][2]int{0: {1, 2}, 1: {3,4}}
函数间传递数组
值传递
//声明一个需要8MB的数组
var array [1e6]int
//传递数组
foo(array)
//接受一个100w个整形值的数组
func foo(array [1e6]int){}
使用指针传递
//声明一个需要8MB的数组
var array [1e6]int
//传递数组
foo(&array)
func foo(array *[1e6]int){}
切片
一种数据结构, 便于使用和管理数据集合. 是围绕动态数组的概念创建的, 就是可变(增长或缩小)数组.
切面的底层内存也是在连续快中分配的, 也能获得索引,迭代.
切片的动态增长是通过内置函数append
实现的.
切片是一个很小的对象, 对底层数组进行了抽象, 并提供操作方法. 包含三个字段: 指向底层数组的指针, 元素个数(长度)和容量.
创建和初始化
//指定长度, 长度等于容量
slice := make([]string, 5)
//指定长度和容量, 只能访问3个, 其余2个通过后期操作合并
slice := make([]string, 3, 5)
slice := []int{1,2,3,4,5}
nil和空切片
nil切片
var slice []int
空切片, 长度为0, 容量为0
slice := make([]int, 0)
slice := []int{}
使用切片
使用一个索引访问数组元素
slice[1]=10
slice[1] #10
使用切片创建切片, 新旧切片共享底层数组
使用两个索引创建新的索引(共用底层数组)
//底层数组长度为5
//长度为2 = 3 - 1
//容量为4 = 5 - 1
newSlice := slice[1:3]
增长
使用内置的append
方法追加, 返回一个新的切片(新的底层数组, 数组指针改变, 长度改变, 容器可能改变)
newSlice := appen(slice[1,3], 6)
三个索引
使用三个索引, 第一个索引表示起始位置, 第二个元素表示起始索引加上希望包括的元素个数 2 + 1 = 3. 第三个索引是起始索引加上容量 2 + 2 = 4.
//从第3个元素开始截取
//长度为1 = 3 - 2
//容量为2 = 4 - 2
newSlice := slice[2,3,4]
迭代切片
使用关键字range
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)
多维切片
函数间的切片传递
映射
存储一系列无序键值对的数据结构, 可以基于键快速检索.
是一个集合, 可以使用类似数组或切片的方式迭代数组. 但是是无序的, 无法预测键值对被返回的顺序.
无序的原因是映射使用了散列表.
实现方式
桶的数据结构: 两个数组. 一个存储散列键的高八位值, 用来做桶定位. 另一个是字节数组, 用于存储键值对. 先一次存储所有的键, 再一次存储所有的值.
将键通过散列函数计算出散列值, 然后通过散列值的高八位定位出桶, 然后在桶的数组里进行存储, 删除或者查找.
键可以是任何类型, 只要这个值可以使用==
运算符做比较. 切片, 函数以及包含切片的机构类型由于具有引用语义, 不能作为映射的键.
创建和初始化
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
使用
空映射
#声明一个空映射
colors := map[string]string[]{}
colors["red"] = "#da1337"
nil映射
//声明为nil映射
var colors map[string]string
colors["red"] = "#da1337" //运行时出错 assignment to entry in nil map
判断是否存在键. 如果不存在exist为false, value为零值. 如果存在exist为true, value为对应的值.
value, exists = colors["blue"]
if exists {
...
}
遍历映射
for key, value range colors {
fmt.printf("Key: %s, Value %s\n", key, value)
}
删除键值对
delete(colors, "red")