《go in action》第4章读书笔记

4.1 数组的内部实现和基础功能

4.1.1 内部实现

数组是一个长度固定的数据类型。

4.1.2 声明和初始化

一旦声明,数组里存储的数据类型的数组长度就都不能改变了。

//声明一个包含5个元素的整型数组
var array [5]int

//声明一个包含5个元素的整型数组, 用初值初始化每个元素
array := [5]int{10,20,30,40,50}

在Go语言中声明变量时,总会使用对应类型的零值来对变量进行初始化。数组也不例外。

如果使用…替代数组的长度,Go语言会根据初始化时数组元素的数量来确定该数组的长度。

array := [...]int{10,20,30,40,50}

也可以指定特定元素的值

array := [5]int{1: 10, 2: 20}

//声明及初始化后,array中的值如下:
[0 10 20 0 0]

4.1.3 使用数组

同样类型的数组可以赋值给另一个数组。

var array1 [5]string

array2 := [5]string{"Red", "Blue", "Green", "Yellow", "Pink"}

//把array2复制到array1,复制之后,两个数组的值完全一样。
array1 = array2

//修改array1不影响array2
array1[2] = "Black"

//修改之后
array1: [Red Blue Black Yellow Pink]
array2: [Red Blue Green Yellow Pink]

数组变量的类型包括数组长度和每个元素的类型。只有这两部分都相同的数组,才是类型相同的数组,才能互相赋值。

4.1.4 多维数组

数组本身只有一个维度,不过可以组合多个数组创建多维数组。

//声明并初始化外层数组中索引为1和3的元素
array := [4][2]int{1: {20, 26}, 3: {16, 18}}

//二维数组值如下:
[[0 0] [20 26] [0 0] [16 18]]

可以独立复制多维数组的某个维度

array := [4][2]int{1: {20, 26}, 3: {16, 18}}
var copyArray [2]int
copyArray = array[1]

//copyArray的值为
[20 26]

4.1.5 在函数间传递数组

在函数之间传递变量时,总是以值的方式传递。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复制,并传递给函数。

好的方式是只传入指向数组的指针。

func foo(array *[1000000]int) {
    ... ...
}

更好的处理这类问题的方式是使用切片

4.2 切片的内部实现和基础功能

切片是围绕动态数组的概念构建的。

4.2.1 内部实现

切片是有3个字段的数据结构。这3个字段分别是指向底层数组的指针、切片访问的元素个数(即长度)和切片允许增长到的元素个数(即容量)。

image

4.4.2 创建和初始化

1. make和切片字面量

//只指定长度,切片的容量和长度相等
slice := make([]string, 3)

//也可以分别指定长度和容量
slice := make([]string, 3, 5)

//通过字面量创建切片
//长度的容量都是3
slice := []int{1,2,3}

//使用空字符串初始化第100个元素
slice1 := []string{99: "ball"}

2. nil和空切片

只要在声明时不做任何初始化,就会创建一个nil切片

// 长度容量均为0
var slice []int

//使用make创建空切片
slice := make([]int, 0)

//使用字面量创建空切片
slice := []int{}

4.2.3使用切片

1. 赋值和切片

slice := []int{10, 20, 30, 40, 50}
newSlice := slice[1:3]

slice和newSlice共享同一底层数组,但通过不同的切片会看到底层数组的不同部分。
image
对于newSlice,底层数组的容量只有4个元素。

需要记住的是,当两个切片共享一个底层数组时,如果一个切片修改了该底层数组的共享部分,另一个切片也能感知到。

与切片容量相关联的元素只能用于增长切片。在使用这部分元素前,必须将其合并到切片的长度量。

2. 切片增长

函数append总是会增加新切片的长度,而容量有可能会改变,也可能不会改变,这取决于被操作的切片的可用容量。

slice := []int{10, 20, 30, 40, 50}
newSlice := slice[1:3]
newSlice = append(newSlice, 60)

image
上例中newSlice在底层数组里还有额外的容量可用,append操作将可用的元素合并到切片的长度,并对其进行赋值。

如果切片的底层数组没有足够的可用容量,append函数会创建一个新的底层数据,将被引用的现有的值复制到新数组里,再追加新值。

函数append会智能地处理底层数组的容量增长。在切片容量小于1000个元素时,总是会成倍地增加容量。一旦元素个数超过100,容量的增长因子会设为1.25,也就是每次增加25%的容量。

3. 创建切片时的3个索引

如果在创建切片时设置切片的容量和长度一样,就可以强制让新切片的第一个append操作创建底层数组,与原有的底层数组分离。

slice := []int{10, 20, 30, 40, 50}
//限制newSlice容量为1
newSlice := slice[1:2:2]

使用…运算符,可以将一个切片的所有元素追加到加一个切片。

s1 := []int{1, 2}
s2 := []int{3, 4}
s2 = append(s1, s2...)

//s2的值为
[1 2 3 4]

4.2.4 多维切片

//创建多维切片
slice := [][]int{{10}, {100, 200}}

创建之后slice的值看起来如下图展现的样子:
image

4.2.5 在函数间传递切片

func foo(slice []int)[]int {
	...
	
	return
}

slice := make([]int, 1000000)
foo(slice)

image

在64位机器上,一个切片需要24字节内存:8字节指针,8字节长度,8字节容量。

由于与切片关联的数据包含在底层数组里,不属于切片本身,所以将切片复制到任意函数时,对底层数组大小都不会有影响。复制时只会复制切片本身。

4.3 映射的内部实现和基础功能

4.3.1 内部实现

即使使用同样的顺序保存键值对,每次迭代映射的时候顺序可能不一样。

映射是一个存储键值对的无序集合。

4.3.2 创建和初始化

可以使用make或映射字面量初始化映射。

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

4.3.3 使用映射

可以通过声明一个未初始化的映射来创建一个值为nil的映射。nil映射不能用于存储键值对。

//创建nil映射
var colors map[string]string
//获取并测试某个键是否存在
value, exists := colors["blue"]
if exists {
	fmt.Println(value)
}

如果想把一个键值对从映射里删除,可以使用delete函数。

4.3.4 在函数间传递映射

在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。

4.4 小结

  • 数组是构造切片和映射的基石
  • Go语言里切片经常用来处理数据的集合,映射用来处理具有键值对结构的数据。
  • 内置函数make可以创建切片和映射,并指定原始的长度和容量。也可以直接使用切片和映射字面量,或者使用字面量作为变量的初始值。
  • 切片有容量限制,不过可以使用内置的append函数扩展容量。
  • 映射的增长没有容量或者任何限制。
  • 内置函数len可以用来获取切片或者映射的长度。
  • 内置函数cap只能用于切片
  • 通过组合,可以创建多维数组和多维切片。也可以使用切片或者其也映射作为映射的值。但是切片不能用作映射的键
  • 将切片或者映射传递给函数成本很小,并且不会复制底层的数据结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值