教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 [go语言]-slice实现的使用和基本原理

[go语言]-slice实现的使用和基本原理

发布时间:2022-02-05   编辑:jiaochengji.com
教程集为您提供[go语言]-slice实现的使用和基本原理等资源,欢迎您收藏本站,我们将为您提供最新的[go语言]-slice实现的使用和基本原理资源

[toc]

<ul><li>Posted by 微博@Yangsc_o </li><li>原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0</li></ul><h1>摘要</h1>

本文主要回顾一下Slice实现的使用和基本原理

<h1>Slice数据结构</h1>

源码包中 src/runtime/slice.go:slice 定义了Slice的数据结构: array指针指向底层数组,len表示切片长度,cap表示底层数组容量。

<pre><code class="lang-go">type slice struct { array unsafe.Pointer len int cap int } </code></pre><h1>使用make创建Slice</h1>

image-20201220190357622

该Slice长度为5,即可以使用下标slice[0] ~ slice[4]来操作里面的元素,capacity为10,表示后续向 slice添加新的元素时可以不必重新分配内存,直接使用预留内存即可。

<h1>使用数组创建Slice</h1>

使用数组来创建Slice时,Slice将与原数组共用一部分内存。 例如,语句 slice := array[5:7] 所创建的Slice,结构如下图所示:

image-20201220190451870

切片从数组array[5]开始,到数组array[7]结束(不含array[7]),即切片长度为2,数组后面的内容都作为切 片的预留内存,即capacity为5。

<ul><li>数组和切片操作可能作用于同一块内存,这也是使用过程中需要注意的地方。</li></ul><h1>Slice 扩容</h1>

使用append向Slice追加元素时,如果Slice空间不足,将会触发Slice扩容,扩容实际上重新一配一块更大的内 存,将原Slice数据拷贝进新Slice,然后返回新Slice,扩容后再将数据追加进去。

例如,当向一个capacity为5,且length也为5的Slice再次追加1个元素时,就会发生扩容,如下图所示:

image-20201220190643601

扩容操作只关心容量,会把原Slice数据拷贝到新Slice,追加数据由append在扩容结束后完成。上图可见,扩容后 新的Slice长度仍然是5,但容量由5提升到了10,原Slice的数据也都拷贝到了新Slice指向的数组中。

扩容容量的选择遵循以下规则: 如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍;
如果原Slice容量大于等于1024,则新Slice容量将扩大为原来的1.25倍; 使用append()向Slice添加一个元素的实现步骤如下:

<ol><li>假如Slice容量够用,则将新元素追加进去,Slice.len ,返回原Slice</li><li>原Slice容量不够,则将Slice先扩容,扩容后得到新Slice</li><li>将新元素追加进新Slice,Slice.len ,返回新的Slice。</li></ol>

image-20201220191523290

<h1>Slice Copy</h1>

使用copy()内置函数拷贝两个切片时,会将源切片的数据逐个拷贝到目的切片指向的数组中,拷贝数量取两个切片长度的最小值。

例如长度为10的切片拷贝到长度为5的切片时,将会拷贝5个元素。 也就是说,copy过程中不会发生扩容。

<h1>特殊切片</h1>

跟据数组或切片生成新的切片一般使用 slice := array[start:end] 方式,这种新生成的切片并没有指定切片的容量, 实际上新切片的容量是从start开始直至array的结束。

比如下面两个切片,长度和容量都是一致的,使用共同的内存地址:

<pre><code class="lang-go">sliceA := make([]int, 5, 10) sliceB := sliceA[0:5] </code></pre>

根据数组或切片生成切片还有另一种写法,即切片同时也指定容量,即slice[start:end:cap], 其中cap即为新 切片的容量,当然容量不能超过原切片实际值,如下所示:

<pre><code class="lang-go">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 </code></pre>

这切片方法不常见,在Golang源码里能够见到,不过非常利于切片的理解。

<h1>总结</h1><ul><li>创建切片时可跟据实际需要预分配容量,尽量避免追加过程中扩容操作,有利于提升性能;</li><li>切片拷贝时需要判断实际拷贝的元素个数</li><li>谨慎使用多个切片操作同一个数组,以防读写冲突</li><li>每个切片都指向一个底层数组</li><li>每个切片都保存了当前切片的长度、底层数组可用容量</li><li>使用len()计算切片长度时间复杂度为O(1),不需要遍历切片</li><li>使用cap()计算切片容量时间复杂度为O(1),不需要遍历切片</li><li>通过函数传递切片时,不会拷贝整个切片,因为切片本身只是个结构体而矣</li><li>使用append()向切片追加元素时有可能触发扩容,扩容后将会生成新的切片</li></ul><h1>参考</h1>

《go专家编程》

<h1>你的鼓励也是我创作的动力</h1>

打赏地址

到此这篇关于“[go语言]-slice实现的使用和基本原理”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
想系统学习GO语言(Golang
[go语言]-slice实现的使用和基本原理
go语言和php的区别是什么?
Go语言爱好者周刊:第 78 期 — 这道关于 goroutine 的题
理解 Go 编程中的 slice
go语言和python哪个难
go html提取纯文本_Go 语言高性能编程
【Golang】01 Go 语言初识
go 语言学习历程
[GO语言基础] 一.为什么我要学习Golang以及GO语言入门普及

[关闭]
~ ~