摘要

本文主要回顾一下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添加一个元素的实现步骤如下:

  1. 假如Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
  2. 原Slice容量不够,则将Slice先扩容,扩容后得到新Slice
  3. 将新元素追加进新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实现的使用和基本原理的更多相关文章

  1. 常用ES6-ES10知识点总结

    在工作中我们会常用到的一些es6-es10的一些特性还记得多少,今天就让我们重新复习一遍 ES6语法 1.Let 1.let声明的变量具有块级作用域, { let a = 1 } console.lo ...

  2. H.264中NAL、Slice与frame意思及相互关系

    H.264中NAL.Slice与frame意思及相互关系 NAL nal_unit_type中的1(非IDR图像的编码条带).2(编码条带数据分割块A).3(编码条带数据分割块B).4(编码条带数据分 ...

  3. javascript基础修炼(9)——MVVM中双向数据绑定的基本原理

    开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 概述 1.1 MVVM模型 MVVM模型是前端单页面应用中非常重要的模型之一,也是Single Page Appl ...

  4. 24 The Go image package go图片包:图片包的基本原理

    The Go image package  go图片包:图片包的基本原理 21 September 2011 Introduction The image and image/color packag ...

  5. 23 The Laws of Reflection 反射定律:反射包的基本原理

    The Laws of Reflection  反射定律:反射包的基本原理 6 September 2011 Introduction 介绍 Reflection in computing is th ...

  6. Node + js实现大文件分片上传基本原理及实践(一)

    _ 阅读目录 一:什么是分片上传? 二:理解Blob对象中的slice方法对文件进行分割及其他知识点 三. 使用 spark-md5 生成 md5文件 四. 使用koa+js实现大文件分片上传实践 回 ...

  7. JS高级学习笔记(9) 之 转:前端路由跳转基本原理

    原文链接: 前端路由跳转基本原理 前述 前端三大框架Angular.React和Vue都推行单页面应用SPA开发模式,这是因为在路由切换时,替换DOM Tree中发生修改的DOM部分,来减少原来因为多 ...

  8. Matlab slice方法和包络法绘制三维立体图

    前言:在地球物理勘探,流体空间分布等多种场景中,定位空间点P(x,y,x)的物理属性值Q,并绘制三维空间分布图,对我们洞察空间场景有十分重要的意义. 1. 三维立体图的基本要件: 全空间网格化 网格节 ...

  9. Ognl表达式基本原理和使用方法

    Ognl表达式基本原理和使用方法 1.Ognl表达式语言 1.1.概述 OGNL表达式 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个 ...

随机推荐

  1. guitar pro 系列教程(六):Guitar Pro音频导出功能之RSE音源

    让我们继续进行guitar pro的教程 上一章节,我们讲解了guitar Pro的播放与显示功能,在Guita pro的音源选择中分为两类,一种是自带的RES高保真音源,一种是MIDI输入音源.如果 ...

  2. XML、XSL、XSLT、DTD、XSD的区别

    前言: 在众神的努力之下,js已经可以跨出浏览器走向不同的领域了 也因为这个,对前端工程师的要求也不仅仅是会写写h5页面做交互.前端涉及的领域越来越广,对开发人员对素质能力要求越高. 以前因设备不同导 ...

  3. 开始使用 java8 的日期工具类

    例如,现有的类(例如java.util.Date和SimpleDateFormatter)不是线程安全的,这会导致用户潜在的并发问题.而新的LocalDate.LocalDateTime.DateTi ...

  4. leetcode 1046

    class Solution {       public int lastStoneWeight(int[] stones) {        MaxHeap s=new MaxHeap(stone ...

  5. MySQL replace into那些隐藏的风险

    目录 replace into时存在主键冲突 replace into时存在唯一索引冲突 replace into时存在主键冲突&唯一索引冲突 存在问题 结论 MySQL中 replace i ...

  6. java实验作业类的定义与描述

    1 //1三角形的定义与描述 2 package test; 3 4 public class sjx { 5 private double a,b,c; 6 7 public sjx(double ...

  7. Docker 入门介绍

    Docker是什么 从发布到现在 docker一直很受关注,在一定程度是改变了软件行业 如果你还不知道 docker 是什么是不是有点out了,接下来我们来介绍docker是什么,解决了什么问题,好处 ...

  8. IDEA社区版(Community)和付费版(UItimate)的区别

    比对类型 Ultimate(终极版,付费) Community(社区版,免费) 语言支持 Java Java Groovy Groovy Kotlin Kotlin Scala(通过插件) Scala ...

  9. 基于java实现的简单网页日历功能,有兴趣得可以把它转换到前端实现

    之前做项目的时候,因为要用到不同日期显示不同的内容,就自己做了一个日期的显示和选择功能,今天抽空把以前的代码理了一下,顺便就把之前做的日期功能给拿出来回顾一下,大家可以提点意见,帮忙完善下设计.先上一 ...

  10. NodeJS+formidable实现文件上传加自动重命名

    前述 本人node初学者,此前使用原生node实现文件上传时遇到了一些困难,只做到了.txt 和.png两中格式的文件可以正常上传,如果上传其他格式文件服务端保存的文件会无法正常打开,原因是对form ...