引子

刚刚写了一个API用到了Golang的Slice,想着用Slice这么久了竟然不知道这到底是个什么东西。
只知道Slice和常规数组相比是可自动变长的,内部到底是怎么实现的呢? 

于是今天就和同事一起研究了一下Golang中Slice的自动扩容机制,这里采用的是Golang1.12版本。
闲话少说,直接开整:

例1:

package main

import (
	"fmt"
)

func main() {
	//定义一个无初始长度的切片
	a := []string{}
	
	for i := 0; i < 6; i++{
		a = append(a, "aaa")
		
		fmt.Println("====")
		//Slice的容量
		fmt.Println(cap(a))
		//Slice的长度
		fmt.Println(len(a))
		fmt.Println("====")
	}
}

输出结果:

1
1
--------
2
2
--------
4
3
--------
4
4
--------
8
5
--------
8
6
--------

结论

由输出结果可以看出,容量达到2之后,当长度大于2时,Slice的容量会自动扩容为原来的2倍。

这里可以得出,Slice的容量会自动扩容的起点是2。

例2:

package main

import (
	"fmt"
)

func main() {
	//定义一个有初始长度的切片,这里为长度为5
	a := make([]string, 5)
	
	for i := 0; i < 10; i++{
		a = append(a, "aaa")
		
		//Slice的容量
		fmt.Println(cap(a))
		//Slice的长度
		fmt.Println(len(a))
		fmt.Println("--------")
	}
}

输出结果:

10
6
--------
10
7
--------
10
8
--------
10
9
--------
10
10
--------
20
11
--------
20
12
--------
20
13
--------
20
14
--------
20
15
--------

结论

看出来例2与例1的区别了吧,当定义一个有初始长度的切片时,切片的容量为初始长度的2倍。

知识点

1.当切片的容量超过2048之后再次扩容每次只会扩容512个,但不是固定的,具体可以看下源码,这里我是使用的Goland,对应包的路径为:
/usr/local/Cellar/go/1.12/libexec/src/runtime/slice.go   方法名:growslice()
2.每一次扩容都会重新开辟一块内存空间,这块内存空间是原来内存空间的2倍,然后将老的数据复制到新开辟的内存空间中,然后释放
老的内存空间。