热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

golang切片底层实现

本章不属于基础部分但是面试经常会问到建议学学,面试官经常问切片是Go中的一种基本的数据结构,使用这种结构可以用来管理数据集合。切片的设计想法是由动态数

本章不属于基础部分但是面试经常会问到建议学学,面试官经常问

切片是 Go 中的一种基本的数据结构,使用这种结构可以用来管理数据集合。切片的设计想法是由动态数组概念而来,为了开发者可以更加方便的使一个数据结构可以自动增加和减少。但是切片本身并不是动态数据或者数组指针。切片常见的操作有 reslice、append、copy。与此同时,切片还具有可索引,可迭代的优秀特性。


切片的数据结构

切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。切片本身是一个只读对象,其工作机制类似数组指针的一种封装。

切片(slice)是对数组一个连续片段的引用,所以切片是一个引用类型(因此更类似于 C/C++ 中的数组类型,或者 Python 中的 list 类型)。这个片段可以是整个数组,或者是由起始和终止索引标识的一些项的子集。需要注意的是,终止索引标识的项不包括在切片内。切片提供了一个与指向数组的动态窗口。

给定项的切片索引可能比相关数组的相同元素的索引小。和数组不同的是,切片的长度可以在运行时修改,最小为 0 最大为相关数组的长度:切片是一个长度可变的数组。

Slice 的数据结构定义如下:

type slice struct {array unsafe.Pointerlen intcap int
}

请添加图片描述

切片的结构体由3部分构成,Pointer 是指向一个数组的指针,len 代表当前切片的长度,cap 是当前切片的容量。cap 总是大于等于 len 的。
请添加图片描述


切片扩容

当一个切片的容量满了,就需要扩容了。怎么扩,策略是什么?

func growslice(et *_type, old slice, cap int) slice {if raceenabled {callerpc :&#61; getcallerpc(unsafe.Pointer(&et))racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))}if msanenabled {msanread(old.array, uintptr(old.len*int(et.size)))}if et.size &#61;&#61; 0 {// 如果新要扩容的容量比原来的容量还要小&#xff0c;这代表要缩容了&#xff0c;那么可以直接报panic了。if cap doublecap {newcap &#61; cap} else {if old.len <1024 {newcap &#61; doublecap} else {for newcap maxSliceCap(et.size) {panic(errorString("growslice: cap out of range"))}var p unsafe.Pointerif et.kind&kindNoPointers !&#61; 0 {// 在老的切片后面继续扩充容量p &#61; mallocgc(capmem, nil, false)// 将 lenmem 这个多个 bytes 从 old.array地址 拷贝到 p 的地址处memmove(p, old.array, lenmem)// 先将 P 地址加上新的容量得到新切片容量的地址&#xff0c;然后将新切片容量地址后面的 capmem-newlenmem 个 bytes 这块内存初始化。为之后继续 append() 操作腾出空间。memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)} else {// 重新申请新的数组给新切片// 重新申请 capmen 这个大的内存地址&#xff0c;并且初始化为0值p &#61; mallocgc(capmem, et, true)if !writeBarrier.enabled {// 如果还不能打开写锁&#xff0c;那么只能把 lenmem 大小的 bytes 字节从 old.array 拷贝到 p 的地址处memmove(p, old.array, lenmem)} else {// 循环拷贝老的切片的值for i :&#61; uintptr(0); i }

扩容策略

先看看扩容策略。

func main() {slice :&#61; []int{10, 20, 30, 40}newSlice :&#61; append(slice, 50)fmt.Printf("Before slice &#61; %v, Pointer &#61; %p, len &#61; %d, cap &#61; %d\n", slice, &slice, len(slice), cap(slice))fmt.Printf("Before newSlice &#61; %v, Pointer &#61; %p, len &#61; %d, cap &#61; %d\n", newSlice, &newSlice, len(newSlice), cap(newSlice))newSlice[1] &#43;&#61; 10fmt.Printf("After slice &#61; %v, Pointer &#61; %p, len &#61; %d, cap &#61; %d\n", slice, &slice, len(slice), cap(slice))fmt.Printf("After newSlice &#61; %v, Pointer &#61; %p, len &#61; %d, cap &#61; %d\n", newSlice, &newSlice, len(newSlice), cap(newSlice))
}

输出结果&#xff1a;

Before slice &#61; [10 20 30 40], Pointer &#61; 0xc4200b0140, len &#61; 4, cap &#61; 4Before newSlice &#61; [10 20 30 40 50], Pointer &#61; 0xc4200b0180, len &#61; 5, cap &#61; 8After slice &#61; [10 20 30 40], Pointer &#61; 0xc4200b0140, len &#61; 4, cap &#61; 4After newSlice &#61; [10 30 30 40 50], Pointer &#61; 0xc4200b0180, len &#61; 5, cap &#61; 8

用图表示出上述过程。

请添加图片描述

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

Go 中切片扩容的策略是这样的&#xff1a;

如果切片的容量小于 1024 个元素&#xff0c;于是扩容的时候就翻倍增加容量。上面那个例子也验证了这一情况&#xff0c;总容量从原来的4个翻倍到现在的8个。

一旦元素个数超过 1024 个元素&#xff0c;那么增长因子就变成 1.25 &#xff0c;即每次增加原来容量的四分之一。

注意&#xff1a;扩容扩大的容量都是针对原来的容量而言的&#xff0c;而不是针对原来数组的长度而言的。


推荐阅读
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了使用Python解析C语言结构体的方法,包括定义基本类型和结构体类型的字典,并提供了一个示例代码,展示了如何解析C语言结构体。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
author-avatar
CC周兵价_667
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有