go 语言中的动态数组(slice),是基于数组实现的,可以相比数组而言更加的灵活。其他语言的 slice 通常仅是一个 API, 但是 go 语言的 slice 不仅仅是一种操作, 也是一种数据结构。

我们先看一下 slice 的数据结构:

type slice struct {
array unsafe.Pointer // 数组指针
len int // 切片长度
cap int // 数组容量
}

源码链接:

https://github.com/golang/go/blob/c379c3d58d5482f4c8fe97466a99ce70e630ad44/src/runtime/slice.go#L15

非常简单的结构组成: 数组指针, 长度, 容量。

slice 的操作:

初始化有 4 种方式:

// 1. 变量声明
var slice []int // 2. 字面量
slice := []int{} // 空的切片
slice2 := []int{1, 2} // 长度为 2 的切片 // 3. 通过 make 创建 slice // 创建一个存储 int 类型的
// len = 10, cap = 20
slice := make([]int, 10, 20) // 4. 通过数组切片, 此时 slice 会与数组共用底层内存 // 获取 array[3][4] 的数据, 且共用 array 后续的存储空间.
// 所以 slice 的 len = 2, cap = 10 - 3 = 7
array := [10]int
slice := array[3:5]

扩容:

当 len > cap 时, slice 会触发扩容, 提高 cap,也就是实际数组容量。

我们来看一下 slice 扩容操作:

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
const threshold = 256
if old.cap < threshold {
// 小的 slice 扩容时 cap 直接翻倍
newcap = doublecap
} else {
// 检测溢出和无限循环
for 0 < newcap && newcap < cap {
// 大的 slice 扩容时扩充 1.25 倍
newcap += (newcap + 3*threshold) / 4
}
if newcap <= 0 {
newcap = cap
}
}
}

源码地址:

https://github.com/golang/go/blob/c379c3d58d5482f4c8fe97466a99ce70e630ad44/src/runtime/slice.go#L188

针对 cap < 256 的 slice,扩容时 newcap = cap * 2

针对 cap >= 256 的 slice, 扩容时 newcap = (cap + 3 * 256) / 4

注意:网上很多文章会写阈值为 1024, 且针对较大的 slice, 采取 1.25 倍扩容策略, 但这种算法是低版本 go 中的计算方式。

那么我们来思考一下, 为什么要这么实现呢?

因为针对较小的 slice,每次扩容增加较充足的容量可以减少内存重分配的次数以及数据迁移的成本。

针对较大的 slice, 每次扩容增加相对较少的容量可以避免内存资源浪费。

添加元素

slice := make([]int, 0)
slice = append(slice, 1) // 添加 1 个元素
slice = append(slice, 2, 3, 4) // 添加多个元素
slice = append(slice, []int{5, 6}...) // 添加一个切片

如果 cap >= len + 1,则直接追加元素到 slice 中, len++,返回 slice。

如果 cap < len + 1, 则扩容 slice 得到新的 slice, 然后追加元素到新的 slice, 新的 slice.len++, 返回新的 slice。

我们可以发现 slice 的操作相比其他数据结构要更加容易理解, 但在使用的时候一定要注意由于与底层数组是通过指针引用导致的共享内存问题。

关于 go 语言中 slice 相关的基础知识就介绍这么多了~

如果您在实际开发过程中遇到过 Python、Golang、数据库、爬虫、分布式、消息队列等方面的难题, 也欢迎在公众号或评论区留言, 我们一起探讨解决方案

如果本篇内容能够帮助到您, 希望您关注我的公众号: 「python 学习爱好者」, 希望与您一起共同成长~

Go 语言入门 3-动态数组(slice)的特性及实现原理的更多相关文章

  1. (待续)C#语言中的动态数组(ArrayList)模拟常用页面置换算法(FIFO、LRU、Optimal)

    目录 00 简介 01 算法概述 02 公用方法与变量解释 03 先进先出置换算法(FIFO) 04 最近最久未使用(LRU)算法 05 最佳置换算法(OPT) 00 简介 页面置换算法主要是记录内存 ...

  2. C语言基础 - 实现动态数组并增加内存管理

    用C语言实现一个动态数组,并对外暴露出对数组的增.删.改.查函数 (可以存储任意类型的元素并实现内存管理) 这里我的编译器就是xcode 分析: 模拟存放 一个 People类 有2个属性 字符串类型 ...

  3. C语言实现使用动态数组实现循环队列

    我在上一篇博客<C语言实现使用静态数组实现循环队列>中实现了使用静态数组来模拟队列的操作. 因为数组的大小已经被指定.无法动态的扩展. 所以在这篇博客中,我换成动态数组来实现. 动态数组能 ...

  4. C语言实现使用动态数组来构造栈结构

    我在面前一篇博客<C语言实现使用静态数组来构造栈结构>中使用了静态数组来模拟栈的操作.静态数组的大小是在代码中写死的.是存储在用户栈上面的,使用起来不灵活.在这篇博客中我会使用动态数组来构 ...

  5. 纯C语言(C89)实现动态数组

    起因 工作很少接触纯C项目,业余写着玩玩,不断雕琢 目标 纯C实现动态数组,提供方便易用泛型接口,避免依赖 实现 完全封装,隐藏结构体细节,不支持栈创建 拷贝存储,轻微性能代价换来易用性 vector ...

  6. 算法入门 - 基于动态数组的栈和队列(Java版本)

    之前我们学习了动态数组的实现,接下来我们用它来实现两种数据结构--栈和队列.首先,我们先来看一下栈. 什么是栈? 栈是计算机的一种数据结构,它可以临时存储数据.那么它跟数组有何区别呢? 我们知道,在数 ...

  7. 使用java语言实现一个动态数组(详解)(数据结构)

    废话不多说,上代码 1.从类名开始(我真是太贴心了) public class Array<E> 首先数组类需要带有泛型,这个不多说.需要注意的是在java中,数组只能存放同一个类型的. ...

  8. C语言入门:一维数组的概要

    数组的概念: 具有相同数据的有序集合 一维数组的定义格式: int a[5]; 类型说明符  数组名(标识符)[常量表达式(长度)]; 一维数组下标 : 数组的下标 从0开始  最大下标值 为 数组的 ...

  9. C++ Primer : 第十二章 : 动态内存之动态数组

    动态数组的分配和释放 new和数组 C++语言和标准库提供了一次分配一个对象数组的方法,定义了另一种new表达式语法.我们需要在类型名后跟一对方括号,在其中指明要分配的对象的数目. int* arr ...

随机推荐

  1. 【Java面试】TCP协议为什么要设计三次握手?

    一个工作5年的粉丝,最近去面试了很多公司,每次都被各种技术原理题问得语无伦次. 由于找了快1个月时间的工作,有点焦虑,来向我求助. 我能做的只是保证每天更新一个面试题,然后问他印象最深刻的一个面试题是 ...

  2. 自己封装的tools.js文件

    /* * 生成指定范围的随机整数 * @param lower 下限 * @param upper 上限 * @return 返回指定范围的随机整数,上/下限值均可取 */ function rand ...

  3. 『现学现忘』Docker基础 — 38、COPY指令和ADD指令

    目录 1.COPY指令 (1)COPY指令说明 (2)COPY指令格式 (3)COPY指令使用 (4)其他 2.ADD指令 (1)ADD指令说明 (2)ADD指令格式 (3)ADD指令使用 (4)不推 ...

  4. Flink1.13.1源码解析-Application on yarn(一)

    本篇文章讲述 Flink Application On Yarn 提交模式下,从命令提交到 AM 容器创建 1.脚本入口 flink run-application -t yarn-applicati ...

  5. 疫情在校学生之——用python对某校园热水服务app进行测试,实现自动免费用水(仅供参考)

    写在前面的过场话: 本文只是对某校园热水服务app做个测试,其实本人并没有做大坏事,并未传播相关技术,文章以下内容的敏感部分会打码,并且相关厂商已经正在进行漏洞修复,大家看看就好.文章后会提供&quo ...

  6. Java去除字符串中 除数字和逗号以外的符号

    例: public static void main(String[] args) { // 去除字符串中 除数字和逗号以外的符号 String str = "_1066,_1068,_10 ...

  7. JS中通过id或者class获取文本内容

    一.JS通过id获取文本内容 二.JS通过class获取文本内容

  8. Tensorflow2 深度学习十必知

    博主根据自身多年的深度学习算法研发经验,整理分享以下十条必知. 含参考资料链接,部分附上相关代码实现. 独乐乐不如众乐乐,希望对各位看客有所帮助. 待回头有时间再展开细节说一说深度学习里的那些道道. ...

  9. Throwable类中3个异常处理的方法和finally代码块

    /* Throwable类中定义了3个异常处理的方法 String getMessage() 返回此 throwable 的简短描述. String toString() 返回此 throwable ...

  10. SimpleDateFormat类介绍和 DateFormat类的format方法和parse方法

    使用 SimpleDateFormat格式化日期 SimpleDateFormat 是一个以语言环境敏感的方式来格式化和分析日期的类.SimpleDateFormat 允许你选择任何用户自定义日期时间 ...