大白话!go语言中的slice底层原理,slice扩容机制

欢迎关注公众号  天天说编程
您的关注是我最大的动力!

1.Slice底层数据结构

Slice的底层由结构体类型构成,包含了指针、长度、容量三个变量。其中指针指向底层数组,每个Slice的都有一个底层数组,底层数组可能是引用的,也可以是自己的。
注意:多个Slice可能引用一个底层数组,造成内存的脏数据。

type slice struct {
	array unsafe.Pointer 指针
	len   int   长度
	cap   int	容量
}

用 make 函数创建的一个 len = 4, cap = 6 的切片。内存空间申请了6个 int 类型的内存大小。由于 len = 4,所以后面2个暂时访问不到,但是容量还是在的。这时候数组里面每个变量都是0 。

make([]int,4,6),4是初始化有值的,容量是6,6是底层数组的长度,4是初始化的值,如果没有初始化,那就给出默认零值。由Slice的指针指向这个底层数组。如下图:

底层数组里面每个元素的值都初始化完成了,下面是字面量创建切片,创建的切片的长度和容量大小相等。

共用同一个底层数组解决办法:

简单来说,copy出独立的底层数组元素,底层数组元素不相关。

现在切片底层数组的共用会引起脏数据的问题,因为s1和s切片会共用同一块内存。比如

func TestSlice1(t *testing.T) {
	s1 := make([]int, 3)
	s1[0] = 11
	s1[1] = 22
	s1[2] = 33
	fmt.Println(s1)
	s := s1[0:2]
	fmt.Println(s)
	fmt.Printf("%T", s)
}

使用切片的 copy​ 方法创建一个新的切片,该新切片拥有独立的底层数组。修改新的切片中的元素不会影响原来的切片元素。

package main

import (
	"fmt"
)

func main() {
	s1 := []int{11, 22, 33}
	fmt.Println("Original slice s1:", s1)

	// 使用 copy 方法创建新的切片 s,拥有独立的底层数组
	s := make([]int, len(s1))
	copy(s, s1)
	fmt.Println("Copied slice s:", s)

	// 修改 s 中的元素不会影响原始切片 s1
	s[0] = 99
	fmt.Println("Modified slice s:", s)
	fmt.Println("Original slice s1 after modification:", s1)
}

2.Slice扩容原理

简单的讲,当len>cap的时候,len是实际底层数组的元素个数,cap是数组的容量,cap的容量更改为2倍。最终会生成一个全新的切片,指针发生变化,原来的指针指向的是原来的数组,原来的数组将不会再被使用,原来的切片指向新的数组。原来的切片被go的垃圾回收机制回收。

关注的有两点,一个是扩容时候的策略,还有一个就是扩容是生成全新的内存地址还是在原来的地址后追加。

从图上我们可以很容易的看出,新的切片和之前的切片已经不同了,因为新的切片更改了一个值,并没有影响到原来的数组,新切片指向的数组是一个全新的数组。并且 cap 容量也发生了变化。

Go 中切片扩容的策略是这样的:

首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。

否则判断,如果旧切片的长度小于1024,则最终容量(newcap)就是旧容量(old.cap)的两倍,即(newcap=doublecap)

否则判断,如果旧切片长度大于等于1024,则最终容量(newcap)从旧容量(old.cap)开始循环增加原来的 1/4,即(newcap=old.cap,for {newcap += newcap/4})直到最终容量(newcap)大于等于新申请的容量(cap),即(newcap >= cap)

如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(cap)。

注意:扩容扩大的容量都是针对原来的容量而言的,而不是针对原来数组的长度而言的。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值