Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)

目 录

  • slice基本
  • 正确使用slice
    • case1
    • case2
    • case3
    • case4
  • 高效使用slice

slice基本

切片slice 相当于“动态数组”,但他并不是数组或数组指针,它通过内部指针和相关属性来引用数组片段,以实现变长的功能。

Slice源码中的数据结构:

type slice struct {
    array unsafe.Pointer // 指向底层数组
    len int // 长度
    cap int // 容量
}

所以,slice代表变长的序列,它的底层是数组。
一个切片由3部分组成:指针、长度和容量。
指针指向底层数组,长度代表slice当前的长度,容量代表底层数组的长度。
换句话说,slice自身维护了一个指针属性,指向它底层数组的某些元素的集合

正确使用slice

这部分内容通过case来讲解,觉着简单的case可跳过。

case1

下面这段代码输出什么?

func main() {
	var s []int
	for i := 0; i < 3; i++ {
		s = append(s,i)
	}
	modifySlice(s)
	fmt.Println(s)
}

func modifySlice(s []int) {
	s[0] = 1024
}

正确输出:[1024 1 2]

其中,
append函数:在切片s末尾添加元素i
fmt:是go的一个标准库,fmt包实现了类似C语言printf和scanf的格式化I/O

case2

下面这段代码输出什么?

func main() {
	var s []int
	for i := 0; i < 3; i++ {
		s = append(s,i)
	}
	modifySlice(s)
	fmt.Println(s)
}
func modifySlice(s []int) {
	s = append(s, 2048)
	s[0] = 1024
}

正确输出:[1024 1 2]

Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)_第1张图片
如图所示,两个S不同,但指向同一个array地址,最后按main中S的长度输出,所以是[1024 1 2]

case3

下面这段代码输出的是什么?

func main() {
	var s []int
	for i := 0; i < 3; i++ {
		s = append(s, i)
	}
	modifySlice(s)
	fmt.Println(s)
}
func modifySlice(s []int) {
	s = append(s, 2048)
	s = append(s, 4096)
	s[0] = 1024
}

正确输出:[0 1 2]

Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)_第2张图片

切片通过 append扩容时,如果切片长度小于当前的容量,那么切片不会扩容,如果追加元素后切片长度大于当前的容量时,切片就会扩容,扩容机制如下:

  • 当扩容之后的元素长度小于 1024 时会以原切片容量的 2 倍的进行扩容;
  • 当扩容之后的元素长度大于 1024 时会以原切片容量的 1.25倍的进行扩容;

Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)_第3张图片

  • 当不需要扩容时,append 函数返回的是原底层数组的原切片(内存地址不变);
  • 当切片需要扩容时,append 函数返回的是新底层数组的新切片(切片内存地址发生了改变);

我们设main中s为S1,modifySlice中的s为S2

所以,Append 4096 后 容量由原来的4扩容成8,开辟了新地址,S2与S1不再指向同一地址。
而更改成1024的,是新地址切片。而输出是main中指向老地址的S1.
2048实际也被写入了原始切片S1,但是println函数是根据len来打印的,所以没有打印出来。

case4

func main() {
	var s []int
	for i := 0; i < 3; i++ {
		s = append(s, i)
	}
	modifySlice(s)
	fmt.Println(s)
}
func modifySlice(s []int) {
	s[0] = 1024
	s = append(s, 2048)
	s = append(s, 4096)
}

正确输出:[1024 1 2]

case4中,1024是更改到还未扩容到老地址上的切片S1中,所以输出的时候有1024.

高效使用slice

1、简单粗暴:直接定义slice,然后进行append操作(性能比较差)
Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)_第4张图片

2、预分配容量然后进行append操作(比第一种好)
Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)_第5张图片

3、容量和长度都分配完再进行append操作(最快的)
Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)_第6张图片

  • 预先分配内存可以提升性能(CPU是比内存更加珍贵的资源)
  • 直接使用index赋值而非append可以提升性能(append其实还是一次调用函数的操作)

你可能感兴趣的:(Go,数据结构,开发语言,go)