golang中slice的扩容机制

阅读此文档默认已经掌握了slice的动态数组原理,如果没有此认知,请阅读https://i6448038.github.io/2018/08/11/array-and-slice-principle/ (本文也是在此链接基础上扩展一些解释说明)。

扩容

slice这种数据结构便于使用和管理数据集合,可以理解为是一种“动态数组”,slice也是围绕动态数组的概念来构建的。既然是动态数组,那么slice是如何扩容的呢?

请记住以下两条规则:

  • 如果切片的容量小于1024个元素,那么扩容的时候slice的cap就翻番,乘以2;一旦元素个数超过1024个元素,增长因子就变成1.25,即每次增加原来容量的四分之一。
  • 如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。

知道了一下规则,请看下面程序,试问输出结果:

1
2
3
4
5
6
7
8
9
10
import (
"fmt"
)
func main(){
array := [4]int{10, 20, 30, 40}
slice := array[0:2]
newSlice := append(append(append(slice, 50), 100), 150)
newSlice[1] += 1
fmt.Println(slice)
}

输出:

1
[10 20]

上述程序中,由于扩容了三次,超过了原始数组的容量,所以把原始数组拷贝过来,对新切片的修改并不影响原始数组的值。从而打印原始数组不影响原数组。

 

如果:

1
2
3
4
5
6
7
8
9
10
import (
"fmt"
)
func main(){
array := [4]int{10, 20, 30, 40}
slice := array[0:2]
newSlice := append(append(slice, 50), 100)
newSlice[1] += 1
fmt.Println(slice)
}

 则输出:

1
[10 21]

由于扩容了两次,没有超过原始数组的容量,所以新切片指针还是指向原始数组,对新切片的修改既对原始数组的修改。从而打印原始数组为修改过的原数组。

 

posted @ 2019-05-07 11:12  JJJJJJJJJJJ.Ge呀!  阅读(4594)  评论(0编辑  收藏  举报