博客
关于我
[Go] Slice的底层自动扩容
阅读量:668 次
发布时间:2019-03-15

本文共 1889 字,大约阅读时间需要 6 分钟。

在Golang语言圣经中,关于数组和Slice的解释有以下要点:

一个Slice实际上是一个轻量级的数据结构,它允许我们轻松访问数组中的子序列(甚至是整体)。Slice的底层实际上是引用了一个数组对象。Slice的结构由三个部分构成:指针、长度和容量。

首先,指针指向Slice实际访问的数组中的第一个元素。这一点需要注意的是,Slice的第一个元素并不一定是数组的第一个元素。这意味着Slice可以从任意位置开始访问数组的元素。

其次,长度表示Slice中当前存储的元素数量。而容量则决定了Slice可以扩展的最大程度。容量是从Slice的当前位置到底层数组末尾的总长度。Golang内置的len和cap函数可以用来获取Slice的长度和容量。

在Golang中,创建Slice有一个特殊的函数make,是make([]T, len, cap)。这个函数会创建一个匿名数组并将其返回作为Slice。虽然Slice引用底层数组的前len个元素,但它的容量却包含整个数组的长度,并保留了额外空间供后续扩展使用。

Slice在扩展元素时,特别是在append操作中,会优化容量管理。为了提升性能,Golang使用了一种叫做_DOUBLE_O Automatically Allocated doubling策略。具体来说,当需要扩展时,Slice会为新分配的空间设置原来的两倍容量,这样在未来添加更多元素时,可以减少内存分配和复制操作的次数,从而保证平均的O(1)时间复杂度。

以下是一个伪代码示例展现Slice扩容的逻辑:

func appendInt(x []int, y int) []int {
zlen := len(x) + 1
if zlen <= cap(x) {
z = x[:zlen]
} else {
zcap := zlen
if zcap < 2 * len(x) {
zcap = 2 * len(x)
}
z = make([]int, zlen, zcap)
copy(z, x)
}
z[len(x)] = y
return z
}

这个函数响应了每次在Slice尾部添加一个新元素的需求。当当前元素的长度加上新元素不会超过原有容量时,直接复制扩展即可。然而,当容量不足时,会为新Slice分配当前长度的两倍容量,以确保未来的增长空间。这既能避免频繁的内存分配,也能保证appending单个元素的操作平均时间保持在O(1)。

基于以上机制,我们可以理解为什么Golang的Slice在内存管理上非常高效。通过预先分配足够的容量,并在需要时动态扩展,最终确保了Slice在大多数情况下的性能表现。

接下来,我们来看看一个实际应用示例:

func main() {
var x, y []int
for i := 0; i < 10; i++ {
y = appendInt(x, i)
fmt.Printf("%d cap=%d\t%v\n", i, cap(y), y)
x = y
}
}

每次循环中,y被赋值为x的新扩展版本,并打印在该时刻的容量和内容。在这个过程中,我们可以看到y的容量如何随着元素添加而逐步扩大:

  • cap=1 -> [0]
  • cap=2 -> [0, 1]
  • cap=2 -> [0, 1](容量保持不变)
  • cap=4 -> [0, 1, 2]
  • cap=4 -> [0,1,2,3]
  • cap=8 -> [0,1,2,3,4]
  • cap=8 -> [0,1,2,3,4,5]
  • cap=8 -> [0,1,2,3,4,5,6]
  • cap=16 -> [0,1,2,3,4,5,6,7]
  • cap=16 -> [0,1,2,3,4,5,6,7,8]

通过这个过程可以看出,每当容量不够时,会进行一次内存重新分配,容量被设置为当前长度的两倍。这种预防性分配策略,使得内存分配的开销被分摊到多次扩展之前,从而平衡了内存分配的平均成本。

总的来说,Golang中的_slice_机制不是一个简单的数组切片,而是一个经过深思熟虑的数据结构。它不仅保证了高效的性能,还为Go语言的内存管理和.zero-initialized策略提供了有力支撑。这也正是Golang在高性能应用开发中脱颖而出的重要原因之一。

转载地址:http://kvimz.baihongyu.com/

你可能感兴趣的文章
Objective-C实现base64加密和base64解密算法(附完整源码)
查看>>
Objective-C实现base64加解密(附完整源码)
查看>>
Objective-C实现base64编码 (附完整源码)
查看>>
Objective-C实现base85 编码算法(附完整源码)
查看>>
Objective-C实现basic graphs基本图算法(附完整源码)
查看>>
Objective-C实现BCC校验计算(附完整源码)
查看>>
Objective-C实现bead sort珠排序算法(附完整源码)
查看>>
Objective-C实现BeadSort珠排序算法(附完整源码)
查看>>
Objective-C实现bellman ford贝尔曼福特算法(附完整源码)
查看>>
Objective-C实现bellman-ford贝尔曼-福特算法(附完整源码)
查看>>
Objective-C实现bellman-ford贝尔曼-福特算法(附完整源码)
查看>>
Objective-C实现BellmanFord贝尔曼-福特算法(附完整源码)
查看>>
Objective-C实现bezier curve贝塞尔曲线算法(附完整源码)
查看>>
Objective-C实现bfs 最短路径算法(附完整源码)
查看>>
Objective-C实现BF算法 (附完整源码)
查看>>
Objective-C实现Bilateral Filter双边滤波器算法(附完整源码)
查看>>
Objective-C实现binary exponentiation二进制幂运算算法(附完整源码)
查看>>
Objective-C实现binary search二分查找算法(附完整源码)
查看>>
Objective-C实现binary tree mirror二叉树镜像算法(附完整源码)
查看>>
Objective-C实现binary tree traversal二叉树遍历算法(附完整源码)
查看>>