首页 > 其他分享 >深入学习go语言(二):数据结构-切片

深入学习go语言(二):数据结构-切片

时间:2023-01-04 17:11:35浏览次数:47  
标签:slice 容量 elem 切片 数组 使用 go 数据结构

在go语言实际开发过程,我们使用更多的是切片而不是数组,数组的固定长度注定了只能在一些特殊场景下才具有优势。

切片是长度可变的,所以切片的类型只有其存储的元素类型这一个维度。并且切片可以在编译期就创建出来,

// NewSlice returns the slice Type with element type elem.
func NewSlice(elem *Type) *Type {
	if t := elem.cache.slice; t != nil {
		if t.Elem() != elem {
			base.Fatalf("elem mismatch")
		}
		if elem.HasTParam() != t.HasTParam() || elem.HasShape() != t.HasShape() {
			base.Fatalf("Incorrect HasTParam/HasShape flag for cached slice type")
		}
		return t
	}

	t := newType(TSLICE)
	t.extra = Slice{Elem: elem}
	elem.cache.slice = t
	if elem.HasTParam() {
		t.SetHasTParam(true)
	}
	if elem.HasShape() {
		t.SetHasShape(true)
	}
	return t
}

可以看到在创建出切片之后,将类型信息保存到了extra中,这样可以在运行时随时获取类型信息。

切片的长度是可变的,但是切片自身的大小是固定的。能够在栈上表示的数据结构都是大小固定的,可以将切片看作一个胖指针,其底层有一个指向数组的指针,切片的数据结构如下:

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

Data指向一块连续的内存,切片的实现实际上就是依赖这个底层数组的,并同时记录了当前存储的元素数量和底层数组的容量信息。切片在使用上屏蔽了这个底层数组,我们可以在运行过程中修改数组的内容以及大小,在容量不足的时候进行自动扩容,上层使用无需关注底层数组的变化。

数组在编译期就可以获得大小以及元素信息,所以编译器对获取数组大小以及访问读写数组进行了简化,直接读写特定的内存位置。不过切片因为是动态的,在运行时确定内容,所以切片的操作需要依赖Go的运行时。

初始化

获取切片有三种方式

  1. 通过下标获得数组或者切片的一部分
  2. 使用字面量初始化切片
  3. 使用关键字make创建切片

使用下标

arr := [3]int{1,2,3}
slice := arr[0: 3]

通过下标初始化,就是创建一个切片结构体,然后让Data指向原数组,设置Len为切取的长度,Cap为数组的长度。这个过程中并没有拷贝底层数组,所以对切片的修改也会影响到原数组。

使用字面量

slice := []int{1, 2, 3}

切片是依赖于一个底层数组的,使用字面量进行初始化的过程相当于使用字面量创建数组然后使用下标对数组进行切片。

  1. 在静态存储区创建一个数组
  2. 将字面量存储到初始化的数组中
  3. 使用下标获得数组的切片

使用make关键字

使用make关键字创建切片,很多工作都需要运行时的参与。

slice := make([]int, len, cap)

需要传递一个必选的长度len以及可选的容量cap。
如果在编译期间能够确定切片的大小,并且切片足够小也不会发生逃逸的时候,就可以在编译期间完成这个切片。例如

slice := make([]int, 3,4)

就可以像使用字面量一样先初始化一个数组,然后用下标获得切片,不过不需要像字面量赋初值。底层数组同样是在静态存储区

var arr [4]int
n := arr[:3]

反之,当切片容量很大或者发生了逃逸的时候,那么切片的底层数组就需要运行时在堆上创建了。于是就需要申请一块连续的内存。内存大小 = 元素大小 * 切片容量。

切片的创建就是使用或者初始化一个数组,然后利用这个数组创建出切片结构体。无非使用make关键字的时候,底层数组可能会在运行时创建在堆上。

访问元素

对切片的访问,一是对切片内元素的访问,一是对切片长度和容量的访问。
对于获取切片长度和容量的操作,我们如果能够在编译期间就能够获得,那么当前就无需运行时的参与,对于这种情况,就可以直接将获取切片长度和容量的操作直接替换成长度和容量。

对切片元素的访问也会在中间代码生成阶段转换成对地址的直接访问。

追加与扩容

使用append向切片末尾添加元素。

slice = append(slice, 1, 2)

虽然使用的时候没有问题,但是我们要知道append返回的是一个新的切片。对于如下的情况

b := append(a, 1, 2)

虽然a,b指向的底层数组是一样的,但是a切片数据结构的len和cap并没有改变,也就是后续遍历a切片的时候,并不会看到新增的1,2元素。
我们在实际使用当中,也总是使用覆盖原切片的写法,但是不需要担心切片拷贝发生的开销,因为编译器对于这种写法进行了优化,可以直接利用原切片的内存空间,修改其len和cap。

当切片容量充足,也就是len < cap的时候,可以直接将元素放到末尾,当容量不足的时候就会进行扩容。
扩容可以简单分为两步:

  1. 确定新切片的大小
  2. 将旧切片的数据拷贝到新切片。

新切片的容量会根据运行时的当前容量选择不同的策略进行扩容:

  1. 如果期望容量大于当前容量的两倍,那么就会使用期望容量。
  2. 如果当前长度小于1024,就会将容量翻倍。
  3. 当前切片长度大于1024,每次增加25%的容量直到大于期望容量。

经过上面的步骤确定了大致容量后,如果切片元素的字节大小为1,8,2的倍数的时候,就会进行内存对齐,按照下面的数组向上取较大的数。

var class_to_size = [_NumSizeClasses]uint16{
    0,
    8,
    16,
    32,
    48,
    64,
    80,
    ...,
}

经过策略和内存对齐确定新切片的容量之后,直接进行内存拷贝 runtime.memmove即可。

标签:slice,容量,elem,切片,数组,使用,go,数据结构
From: https://www.cnblogs.com/smarticen/p/16933487.html

相关文章

  • 对于goland相对较新一些版本新建项目时没用go mod模式选项的坑
    前言对于一些小白在网上看很早的一些go视频,使用goland2020.3.x版本或者其之前版本创建新项目,里面会有GOModules(vgo)这个选项,也就是gomod模式创建新项目,然而对于现在相对新......
  • 算法与数据结构 学习流程
    嵌入式LINUX中算法与数据结构应用比较广泛,需要学习及熟练掌握:  推荐一些算法书籍。入门系列入门的同学,我建议你不要过度追求上去就看经典书。不要一来就拿着《算......
  • electron + go 如何从sqlite获取数据
    我现在的数据在sqlite中,保存在mac本地的一个文件中。用了electron+vue搭建了一个客户端。我大概希望是这样的逻辑,先加载本地db文件,然后再获取数据。这里就有一个问题,我......
  • Trigonometric Series
    TrigonometricseriesFromWikipedia,thefreeencyclopediaJumpto:​​navigation​​​,​​​search​​In​​mathematics​​,atrigonometricser......
  • golang 断言
    开心一刻    小学时,因为淘气,再加上成绩不好,爸妈经常打我,有次爷爷奶奶来家里,看到我挨打,心疼啊,就把我接过去住!没一个月,我就被送回父母家了,爷爷奶奶进门第一句话就是:......
  • 【最新】最全的Google镜像站地址
    写在前面遇到问题,有时光靠Baidu有时解决不了问题,所以需要求助Google,里面有好多国外网友的博客、StackoverFlow、GithubIssues、官方文档等等的大量一手英文资料,如果你想要......
  • golang的mutex互斥锁
    什么是计算机的锁?以前经常遇到锁的时候,计算机的锁到底是一个普通变量,还是一个数据总线的一个开关。网上查,一上来都是一大推的云里雾里专业术语。看了也不懂,怪本人计算机知识......
  • MongoDB6.0的安装「2023年」
    你好,我是悦创。优质原文格式:https://bornforthis.cn/column/crawler/supplement/mongodb-install.html点进去有惊喜。吐槽,这篇博客的产生是因为本人被MongoDB的安装坑......
  • unity+高德定位=pokemon go 山寨demo安卓版
    这两周尝试了下用高德地理定位和Unity来做个山寨的pokemongo的demo,只能在安卓下使用。游戏过程视频:​​http://www.bilibili.com/video/av6836823/​​场景一这里是获取......
  • Implementing a message sound notification effect in an online customer support l
    Implementingamessagesoundnotificationeffectinselfhostedprivatesupportchatsoftwarecanbringmanybenefits,including:Improvingtheuserexperience......