go-slice实现的使用和基本原理
- Posted by 微博@Yangsc_o
- 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
摘要
本文主要回顾一下Slice实现的使用和基本原理
Slice数据结构
源码包中 src/runtime/slice.go:slice 定义了Slice的数据结构: array指针指向底层数组,len表示切片长度,cap表示底层数组容量。
type slice struct {
array unsafe.Pointer
len int
cap int
}
使用make创建Slice
该Slice长度为5,即可以使用下标slice[0] ~ slice[4]来操作里面的元素,capacity为10,表示后续向 slice添加新的元素时可以不必重新分配内存,直接使用预留内存即可。
使用数组创建Slice
使用数组来创建Slice时,Slice将与原数组共用一部分内存。 例如,语句 slice := array[5:7] 所创建的Slice,结构如下图所示:
切片从数组array[5]开始,到数组array[7]结束(不含array[7]),即切片长度为2,数组后面的内容都作为切 片的预留内存,即capacity为5。
- 数组和切片操作可能作用于同一块内存,这也是使用过程中需要注意的地方。
Slice 扩容
使用append向Slice追加元素时,如果Slice空间不足,将会触发Slice扩容,扩容实际上重新一配一块更大的内 存,将原Slice数据拷贝进新Slice,然后返回新Slice,扩容后再将数据追加进去。
例如,当向一个capacity为5,且length也为5的Slice再次追加1个元素时,就会发生扩容,如下图所示:
扩容操作只关心容量,会把原Slice数据拷贝到新Slice,追加数据由append在扩容结束后完成。上图可见,扩容后 新的Slice长度仍然是5,但容量由5提升到了10,原Slice的数据也都拷贝到了新Slice指向的数组中。
扩容容量的选择遵循以下规则: 如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍;
如果原Slice容量大于等于1024,则新Slice容量将扩大为原来的1.25倍; 使用append()向Slice添加一个元素的实现步骤如下:
- 假如Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
- 原Slice容量不够,则将Slice先扩容,扩容后得到新Slice
- 将新元素追加进新Slice,Slice.len++,返回新的Slice。
Slice Copy
使用copy()内置函数拷贝两个切片时,会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。
例如长度为10的切片拷贝到长度为5的切片时,将会拷贝5个元素。 也就是说,copy过程中不会发生扩容。
特殊切片
跟据数组或切片生成新的切片一般使用 slice := array[start:end] 方式,这种新生成的切片并没有指定切片的容量, 实际上新切片的容量是从start开始直至array的结束。
比如下面两个切片,长度和容量都是一致的,使用共同的内存地址:
sliceA := make([]int, 5, 10)
sliceB := sliceA[0:5]
根据数组或切片生成切片还有另一种写法,即切片同时也指定容量,即slice[startcap], 其中cap即为新 切片的容量,当然容量不能超过原切片实际值,如下所示:
sliceA := make([]int, 5, 10) //length = 5; capacity = 10
sliceB := sliceA[0:5] //length = 5; capacity = 10
sliceC := sliceA[0:5:5] //length = 5; capacity = 5
这切片方法不常见,在Golang源码里能够见到,不过非常利于切片的理解。
总结
- 创建切片时可跟据实际需要预分配容量,尽量避免追加过程中扩容操作,有利于提升性能;
- 切片拷贝时需要判断实际拷贝的元素个数
- 谨慎使用多个切片操作同一个数组,以防读写冲突
- 每个切片都指向一个底层数组
- 每个切片都保存了当前切片的长度、底层数组可用容量
- 使用len()计算切片长度时间复杂度为O(1),不需要遍历切片
- 使用cap()计算切片容量时间复杂度为O(1),不需要遍历切片
- 通过函数传递切片时,不会拷贝整个切片,因为切片本身只是个结构体而矣
- 使用append()向切片追加元素时有可能触发扩容,扩容后将会生成新的切片
参考
《go专家编程》
你的鼓励也是我创作的动力
go-slice实现的使用和基本原理的更多相关文章
- 常用ES6-ES10知识点总结
在工作中我们会常用到的一些es6-es10的一些特性还记得多少,今天就让我们重新复习一遍 ES6语法 1.Let 1.let声明的变量具有块级作用域, { let a = 1 } console.lo ...
- H.264中NAL、Slice与frame意思及相互关系
H.264中NAL.Slice与frame意思及相互关系 NAL nal_unit_type中的1(非IDR图像的编码条带).2(编码条带数据分割块A).3(编码条带数据分割块B).4(编码条带数据分 ...
- javascript基础修炼(9)——MVVM中双向数据绑定的基本原理
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 概述 1.1 MVVM模型 MVVM模型是前端单页面应用中非常重要的模型之一,也是Single Page Appl ...
- 24 The Go image package go图片包:图片包的基本原理
The Go image package go图片包:图片包的基本原理 21 September 2011 Introduction The image and image/color packag ...
- 23 The Laws of Reflection 反射定律:反射包的基本原理
The Laws of Reflection 反射定律:反射包的基本原理 6 September 2011 Introduction 介绍 Reflection in computing is th ...
- Node + js实现大文件分片上传基本原理及实践(一)
_ 阅读目录 一:什么是分片上传? 二:理解Blob对象中的slice方法对文件进行分割及其他知识点 三. 使用 spark-md5 生成 md5文件 四. 使用koa+js实现大文件分片上传实践 回 ...
- JS高级学习笔记(9) 之 转:前端路由跳转基本原理
原文链接: 前端路由跳转基本原理 前述 前端三大框架Angular.React和Vue都推行单页面应用SPA开发模式,这是因为在路由切换时,替换DOM Tree中发生修改的DOM部分,来减少原来因为多 ...
- Matlab slice方法和包络法绘制三维立体图
前言:在地球物理勘探,流体空间分布等多种场景中,定位空间点P(x,y,x)的物理属性值Q,并绘制三维空间分布图,对我们洞察空间场景有十分重要的意义. 1. 三维立体图的基本要件: 全空间网格化 网格节 ...
- Ognl表达式基本原理和使用方法
Ognl表达式基本原理和使用方法 1.Ognl表达式语言 1.1.概述 OGNL表达式 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个 ...
随机推荐
- 在家看电影音效太差?Boom 3D帮你轻松升级
新片上映后,很多人都会选择去电影院观看,一是为了第一时间看到电影,还有一个原因就是电影院的音效往往可以让人身临其境,更好地感受电影的氛围.那如果在家刷片我们该怎么办呢? 我们可以使用Boom 3D这款 ...
- jmp使用
jps -l jmap 36429 jmap -heap 36429 jmap -histo:live 36429 jmap -clstats 36429 jmap -finalizerinfo 3 ...
- Foreground-aware Image Inpainting
引言 语义分割得到边缘信息指导修复其三 存在问题:现在的图像修复方法主要的通过周围像素来修复,当修复区域与前景区域(显著物体)有交叠时,由于修复区域缺失前景与背景的时间内容导致修复结果不理想. 提出方 ...
- 不使用 MQ 如何实现 pub/sub 场景?
hello,大家好,我是小黑,又和大家见面啦~~ 在配置中心中,有一个经典的 pub/sub 场景:某个配置项发生变更之后,需要实时的同步到各个服务端节点,同时推送给客户端集群. 在之前实现的简易版配 ...
- MIT-6.004计算结构(2019年春)
L01: Introduction 略 L02: RISC-V Assembly 1.计算机处理器主要有三部分组成:内存.寄存器.算数逻辑单元 算数逻辑单元与寄存器通信,寄存器与内存通信,而算术逻辑单 ...
- celery异步发送短信
1.使用celery异步发送短信 1.1 在 celery_task/mian.py 中添加发送短信函数 # celery项目中的所有导包地址, 都是以CELERY_BASE_DIR为基准设定. # ...
- Spark存储Parquet数据到Hive,对map、array、struct字段类型的处理
利用Spark往Hive中存储parquet数据,针对一些复杂数据类型如map.array.struct的处理遇到的问题? 为了更好的说明导致问题的原因.现象以及解决方案,首先看下述示例: -- 创建 ...
- 再见!【JAVA GUI】
1.GUI概述 图形用户界面(Graphical User Interface,简称 GUI,又 称图形用户接口)是指采用图形方式显示的计算机操作用户界面. java作为一种面向对象的程序设计语言,它 ...
- 洛谷P3906 Hoof Paper, Scissor (记忆化搜索)
这道题问的是石头剪刀布的的出题问题 首先不难看出这是个dp题 其次这道题的状态也很好确定,之前输赢与之后无关,确定三个状态:当前位置,当前手势,当前剩余次数,所以对于剪刀,要么出石头+1分用一次机会, ...
- 2016 ACM/ICPC ECNA Regional I.Waif Until Dark(最大流)
这是一道ECNA的16年题,问有n个小朋友,m个玩具,不同孩子有不同喜好的玩具,每个玩具可能属于一个类别,同一类别的玩具最多只能用一定次数,问最大匹配 这个就很裸的二分图,掏出dinic板子,首先最后 ...