• 通过上 小结 学习主要加深了对:常用源码文件(命令、库、测试)写法、程序实体(常量、变量、结构体、函数、接口, 尤其是变量)以及相关的各种概念和编程技巧(类型推断、变量重声明、可重名变量、类型断言、类型转换、别名类型和潜在类型等) 的理解。
  • 接下来进入模块二:数组与切片,主要侧重于理解数组与切片底层的实现方式、存储结构、以及这两个集合类类型的区别是什么?

专栏:07 | 数组和切片

数组和切片

核心概念:

  • 数组:长度固定,数组的长度在声明的时候必须给定,且之后不会再改变。可以说,数组的长度是其类型的一部分。数组的容量永远等于其长度。

  • 切片:长度可变,切片的长度可自动随其中元素数量的增长而增长,但不会随元素数量的减少而减少。每个切片的底层数据结构中,一定会包含一个数组。

Note: 在 Go 语言中,我们判断所谓的 传值 或者 传引用 只要看被传递的值的类型就好了。如果传的值是引用类型,那么就是 传引用。如果传的值是值类型的,那么就是 传值

引出问题:

  • 怎样正确估算切片的长度和容量?

Note: 切片的容量代表了它底层数组的长度,但这仅限于使用 make 函数或者切片值字面量初始化切片的情况。更通用的规则是:一个切片的容量可以被看作是透过这个窗口最多可以看到的底层数组中元素的个数。

Click Here for Demo Code

知识扩展

  • 问题 1:怎样估算切片容量的增长?

一旦一个切片无法容纳更多的元素,Go 语言就会想办法扩容。但它并不会改变原来的切片,而是会生成一个容量更大的切片,然后将把原有的元素和新元素一并拷贝到新切片中去。

在一般情况下,你可以简单的认为新切片的容量将会是原切片容量的 2 倍。

但是,当原切片的长度大于或等于 1024 时,Go 语言将会以原容量的 1.25 倍作为新容量的基准。

新容量基准会被调整(不断地与 1.25 相乘),直到结果不小于原长度与要追加的元素数量之和。最终,新容量往往会比新长度大一些,当然,相等也是可能的。

另外,如果我们一次追加的元素过多,以至于使新长度比原容量的 2 倍还要大,那么新容量就会以新长度为基准(Note: 有时会出现与预期计算的不相等的情况,因为底层算法还要考虑到按字节对齐)。

  • 问题 2:切片的底层数组什么时候会被替换?

确切的说,一个切片的底层数组永远不会被替换。为什么? 虽然在扩容的时候 Go 语言一定会生成新的底层数组,但是它也同时生成了新的切片。它是把新切片作为了新底层数组的窗口,而没有对原切片以及其底层数组做任何改动。

请记住,在无需扩容时,append() 函数返回的是指向原底层数组的新切片,而在需要扩容时,append() 函数返回的是指向新底层数组的新切片。

顺便说一下,只要新长度不会超过切片的原容量,那么使用 append 函数对其追加元素的时候就不会引起扩 容。这只会使紧邻切片窗口右边的(底层数组中的)元素被新的元素替换掉。

思考题:

  • 如果有多个切片指向了同一个底层数组,那么你认为应该注意些什么?
  • 怎么沿用 扩容 的思想对切片进行 缩容? 请写出代码

Click Here for Demo Code