Golang Slice 结构体

写这个的初衷是因为看到一个 b站的UP主(幼麟实验室)做 Golang 的视频有感而生,想通过视频深入剖析一下内容,顺便当作一个输出,记录自己学习的过程。

up主的连接如下:幼麟实验室

Slice 结构体

SlicelenSlicecapslice
// runtime/slice.go
type slice struct {
	array unsafe.Pointer
	len   int
	cap   int
}

通过下面的方式来定义一个整数切片

var ints []int
intsdatanil00[nil, 0, 0]makeints
ints = make([]int, 2) // len = 2
make
ints = make([]int, 2,5) // len = 2. cap = 5

第三个参数为切片的容量。所以,用户是可以自定义该切片的容量的。你们也可以通过如下实验来检测这个的正确性。

import "fmt"

func main() {
	var ints []int
	ints = make([]int, 2)
	fmt.Println(" len: ", len(ints), " cap: ", cap(ints))

	ints = make([]int, 2, 5)
	fmt.Println(" len: ", len(ints), " cap: ", cap(ints))
}

output
>>> len: 2 cap: 2
>>> len: 2 cap: 5
lenpanic
func main() {
	var ints []int
	ints = make([]int, 2, 5)
	fmt.Println("len:", len(ints), "cap:", cap(ints))

	fmt.Println(ints[2])
}

output
>>> len: 2 cap: 2
>>> panic: runtime error: index out of range [2] with length 2
ints
ints := new([]int)
intsdatanilintsappend
ints = append(ints, 1)
ints[data, 1, 1]data11
func main() {
	ints := new([]int)
	fmt.Println(&ints)
	fmt.Println("len:", len(*ints), "cap:", cap(*ints))

	*ints = append(*ints, 1)
	fmt.Println("len:", len(*ints), "cap:", cap(*ints))
}

output
>>> 0xc0000ce018
>>> len: 0 cap: 0
>>> len: 1 cap: 1
ints00append11

Slice 结构体获取“子”切片

使用过 python 的都知道,python 数组可以直接通过索引来获得子数组,如下所示:

nums = [0,1,2,3,4]
sub = nums[1:3]
print(sub)

output
>>> [1,2]

golang 中,切片也可以通过如下的方式来获取子数组。

ints := [10]int{1,2,3,4,5,6,7,8,9,10}
var s1 = ints[1:4]
var s2 = ints[7:]
s1s2ints[1:4]3s1arr13dataarr[1][1-9]9s2
func main() {
	arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	for i := 0; i < 10; i++ {
		println(&arr[i])
	}
	println("=====================")
	
	var s1 []int = arr[1:4]
	var s2 []int = arr[7:]

	a1 := &s1[0]
	println(a1)
	a2 := &s2[0]
	println(a2)
}

output
>>> 0xc000077f20
>>> 0xc000077f28
>>> 0xc000077f30
>>> 0xc000077f38
>>> 0xc000077f40
>>> 0xc000077f48
>>> 0xc000077f50
>>> 0xc000077f58
>>> 0xc000077f60
>>> 0xc000077f68
>>> =====================
>>> 0xc000077f28
>>> 0xc000077f58
64int8arr8s10xc000077f28arr[1]0xc000077f58arr[7]

重点

s1s1[data, 3, 9][data, 4, 9]s1arr[1]appendarr
func main() {
	arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Println(arr)

	println("=====================")

	var s1 []int = arr[1:4]
	fmt.Println(&s1[0], " len: ", len(s1), " cap: ", cap(s1))

	s1 = append(s1, 1)
	fmt.Println(&s1[0], " len: ", len(s1), " cap: ", cap(s1))
	
	println("=====================")
	fmt.Println(arr)
}

output
>>> [0 1 2 3 4 5 6 7 8 9]
>>> =====================
>>> 0xc0000b00a8  len:  3  cap:  9
>>> 0xc0000b00a8  len:  4  cap:  9
>>> =====================
>>> [0 1 2 3 1 5 6 7 8 9]
arr[1]s1slice1arr[4]slice
func main() {
	arr := [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	fmt.Println(arr)

	println("=====================")

	var s1 []int = arr[7:]
	fmt.Println(&s1[0], " len: ", len(s1), " cap: ", cap(s1))

	s1 = append(s1, 1)
	fmt.Println(&s1[0], " len: ", len(s1), " cap: ", cap(s1))

	println("=====================")
	fmt.Println(arr)
	println("=====================")
	fmt.Println(s1)
}

output
>>> [0 1 2 3 4 5 6 7 8 9]
>>> =====================
>>> 0xc0000b00d8  len:  3  cap:  9
>>> 0xc0000c0090  len:  4  cap:  6
>>> =====================
>>> [0 1 2 3 4 5 6 7 8 9]
>>> =====================
>>> [7 8 9 1]

可以看出,此时扩容前后数组的底层数组位置发生了变化,且增加不再影响原来的数组了。

Slice 结构体扩容机制

s23 -> 46sliceslice
// runtime/slice.go
func growslice(et *_type, old slice, cap int) slice {
	//...
	newcap := old.cap
	doublecap := newcap + newcap
	if cap > doublecap {
		newcap = cap
	} else {
		if old.cap < 1024 {
			newcap = doublecap
		} else {
			//...
			for 0 < newcap && newcap < cap {
				newcap += newcap / 4
			}
		}
	}
	//...
}
newcap > 2 * oldcapnewcapnewcapnewcap
ints := make([]int, 2)
ints = append(ints, 2, 3, 4)
252 * 2 < 55oldcap < 102410241.25
short_ints := make([]int, 2)
short_ints = append(ints, 2) // 这个为翻倍

long_ints := make([]int, 2048)
long_ints = append(ints, 2) // 这个为 1.25 倍
s265
新容量 * 类型大小
ints := make([]int, 2)
ints = append(ints, 2, 3, 4)
584048486int6
func main() {
	ints := make([]int, 2)
	fmt.Println(&ints[0], " len: ", len(ints), " cap: ", cap(ints))
	ints = append(ints, 2, 3, 4)
	fmt.Println(&ints[0], " len: ", len(ints), " cap: ", cap(ints))

	println("=====================")

	ints2 := make([]int, 2)
	fmt.Println(&ints2[0], " len: ", len(ints2), " cap: ", cap(ints2))
	ints2 = append(ints2, 2)
	fmt.Println(&ints2[0], " len: ", len(ints2), " cap: ", cap(ints2))

	println("=====================")

	ints3 := make([]int, 1024)
	fmt.Println(&ints3[0], " len: ", len(ints3), " cap: ", cap(ints3))
	ints3 = append(ints3, 2)
	fmt.Println(&ints3[0], " len: ", len(ints3), " cap: ", cap(ints3))
}

output
>>> =====================
>>> 0xc0000180a0  len:  2  cap:  2
>>> 0xc00000a360  len:  5  cap:  6
>>> =====================
>>> 0xc0000180d0  len:  2  cap:  2
>>> 0xc00000e200  len:  3  cap:  4
>>> =====================
>>> 0xc00007c000  len:  1024  cap: 1024
>>> 0xc000100000  len:  1025  cap: 1280
32410241.251280128010241.25
ints2ints

最后

不得不说,前面提到的 UP 主做的视频真的很适合 golang 开发者,但是可能需要有一定理解再去看会比较有收获。还有就是,打博文太难了…