《Go语言精进之路》读书笔记 | 了解切片实现原理并高效使用

Posted COCOgsta

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Go语言精进之路》读书笔记 | 了解切片实现原理并高效使用相关的知识,希望对你有一定的参考价值。

书籍来源:《Go语言精进之路:从新手到高手的编程思想、方法和技巧》

一边学习一边整理读书笔记,并与大家分享,侵权即删,谢谢支持!

附上汇总贴:《Go语言精进之路》读书笔记 | 汇总_COCOgsta的博客-CSDN博客


slice,中文多译为切片,是Go语言在数组之上提供的一个重要的抽象数据类型。在Go语言中,对于绝大多数需要使用数组的场合,切片实现了完美替代。并且可提供更灵活、更高效的数据序列访问接口。

13.1 切片究竟是什么

切片之于数组就像是文件描述符之于文件。在Go语言中,数组更多是“退居幕后”,承担的是底层存储空间的角色;而切片则走向“前台”,为底层的存储(数组)打开了一个访问的“窗口”(见图13-1)。

图13-1 切片打开了访问底层数组的“窗口”

可以称切片是数组的“描述符”。切切片这个描述符是固定大小的,无论底层的数组元素类型有多大。

可以用下面的语句创建一个切片实例s:

s := make([]byte, 5)
复制代码

图13-2展示了切片s在运行时层面的内部表示。

图13-2 切片运行时表示(新切片)

编译器会自动为切片建立一个底层数组,如果没有在make中指定cap参数,那么cap = len,即编译器建立的数组长度为len。

通过语法u[low: high]创建对已存在数组进行操作的切片,这被称为数组的切片化(slicing):

u := [10]byte11, 12, 13, 14, 15, 16, 17, 18, 19, 20
s := u[3:7]
复制代码

图13-3展示了切片s的内部。

图13-3 切片运行时表示(以已有数组为底层存储的切片)

切片的容量值(cap)取决于底层数组的长度。从切片s的第一个元素s[0],即u[3]到数组末尾一共有7个存储元素的槽位,因此切片s的cap为7。

还可以通过语法s[low: high]基于已有切片创建新的切片,这被称为切片的reslicing,如图13-5所示。新创建的切片与原切片同样是共享底层数组的,并且通过新切片对数组的修改也会反映到原切片中。

图13-5 切片运行时表示(基于切片s1建立新切片s2)

13.2 切片的高级特性:动态扩容

Go切片还支持一个重要的高级特性:动态扩容。零值切片可以通过append预定义函数进行元素赋值操作:

var s []byte // s被赋予零值nil
s = append(s, 1)
复制代码

我们打印出每次append操作后切片s的len和cap值:

// chapter3/sources/slice_append.go
var s []int  // s被赋予零值nil
s = append(s, 11)
fmt.Println(len(s), cap(s)) //1 1
s = append(s, 12)
fmt.Println(len(s), cap(s)) //2 2
s = append(s, 13)
fmt.Println(len(s), cap(s)) //3 4
s = append(s, 14)
fmt.Println(len(s), cap(s)) //4 4
s = append(s, 15)
fmt.Println(len(s), cap(s)) //5 8
复制代码

我们看到切片s的len值是线性增长的,但cap值却呈现出不规则的变化。通过图13-6我们更容易看清楚多次append操作究竟是如何让切片进行动态扩容的。

图13-6 切片的动态扩容

我们看到append会根据切片对底层数组容量的需求对底层数组进行动态调整。

在当前底层数组容量无法满足的情况下,按一定算法动态分配新的数组。新数组建立后,append会把旧数组中的数据复制到新数组中,成为切片的底层数组,旧数组后续会被垃圾回收掉。

13.3 尽量使用cap参数创建切片

从append的原理中可以看到重新分配底层数组并复制元素的操作代价还是挺大的,一种有效的方法是根据切片的使用场景对切片的容量规模进行预估,并在创建新切片时将预估出的切片容量数据以cap参数的形式传递给内置函数make:

s := make([]T, len, cap)
复制代码

以上是关于《Go语言精进之路》读书笔记 | 了解切片实现原理并高效使用的主要内容,如果未能解决你的问题,请参考以下文章

《Go语言精进之路》读书笔记 | 了解string实现原理并高效使用

《Go语言精进之路》读书笔记 | 了解Go语言的诞生与演进

《Go语言精进之路》读书笔记 | 了解Go语言控制语句惯用法及使用注意事项

《Go语言精进之路》读书笔记 | 汇总

《Go语言精进之路》读书笔记 | 选择适当的Go语言版本

《Go语言精进之路》读书笔记 | 使用iota实现枚举常量