一、数组内部实现
-
在 Go 语言里,数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连续块。数组存储的类型可以是内置类型,如整型或者字符串,也可以是某种结构类型。
-
下图中可以看到数组的表示。灰色格子代表数组里的元素,每个元素都紧邻另一个元素。 每个元素包含相同的类型,这个例子里是整数,并且每个元素可以用一个唯一的索引(也称下标 或标号)来访问。
-
数组是一种非常有用的数据结构,因为其占用的内存是连续分配的。由于内存连续,CPU 能把正在使用的数据缓存更久的时间。而且内存连续很容易计算索引,可以快速迭代数组里的所有元素。
-
数组的类型信息可以提供每次访问一个元素时需要在内存中移动的距离。既然数组的每 个元素类型相同,又是连续分配,就可以以固定速度索引数组中的任意数据,速度非常快。
- 在函数间传递数组: 根据内存和性能来看,在函数间传递数组是一个开销很大的操作。在函数之间传递变量时, 总是以值的方式传递的。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复制,并传递给函数。将数组的地址传入函数,只需要在栈上分配 8 字节的内存给指针就可以。 这个操作会更有效地利用内存,性能也更好。不过要意识到,因为现在传递的是指针, 所以如果改变指针指向的值,会改变共享的内存。
二、切片内部实现
-
切片可以按需自动增长和缩小。切片的动态增长是通过内置函数 append 来实现的。还可以通过对切片再次切片来缩小一个切片的大小。因为切片的底层内存也是在连续块中分配的,所以切片还能获得索引、迭代以及为垃圾回收优化的好处。
- 切片是一个很小的对象,对底层数组进行了抽象,并提供相关的操作方法。切片有 3 个字段的数据结构,这些数据结构包含 Go 语言需要操作底层数组的元数据(如下图)。
- 使用切片创建切片:
-
如果切片的底层数组没有足够的可用容量,append 函数会创建一个新的底层数组,将被引用的现有的值复制到新数组里,再追加新的值,当这个 append 操作完成后,newSlice 拥有一个全新的底层数组,这个数组的容量是原来的两倍,函数 append 会智能地处理底层数组的容量增长。在切片的容量小于 1000 个元素时,总是会成倍地增加容量。一旦元素个数超过 1000,容量的增长因子会设为 1.25,也就是会每次增加 25%的容量。随着语言的演化,这种增长算法可能会有所改变,如下图:
-
使用3个索引创建切片(slice[i:j:k]) 长度=j-i 容量=k-i
-
迭代切片:关键字 range 会返回两个值(第一个值是当前迭代到的索引位置,第二个值是该位置对应元素值)的一份副本。如下图:
|
请发表评论