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. 前缀和与差分(Acwing795-798)

    一维前缀和 Acwing795.前缀和 #include <iostream> using namespace std; const int N = 100010; int n, m; i ...

  2. electron-vue 项目启动动态获取配置文件中的后端服务地址

    前言 最近的项目迭代中新增一个需求,需要在electron-vue 项目打包之后,启动exe 可执行程序的时候,动态获取配置文件中的 baseUrl 作为服务端的地址.electron 可以使用 no ...

  3. RPA 微信财务报销机器人 竹间智能

    1.首先通过微信对话机器人收集报销信息及内容 2.上传发票并进行OCR识别 3.收集相关的出差信息,支持对话中修改内容 4.完成信息收集后,后台RPA机器人执行报销操作,并发送确认邮件 5.收到邮件后 ...

  4. UiPath直播课程

    UiPath实现拉勾招聘信息的抓取和汇总 https://www.bilibili.com/video/av81859882 UiPath企业框架Reframework的介绍和使用 https://w ...

  5. python小题目练习(十二)

    题目:如下图所示 代码展示: """Author:mllContent:春节集五福Date:2020-01-17"""import rand ...

  6. 微信小程序接口请求/form-data/单文件、多文件上传

    1.普通的微信请求封装 1 const http = (options) =>{ 2 return new Promise((resolve,reject) => { 3 wx.reque ...

  7. Selenium指定浏览器路径

    ChromeOptions options = new ChromeOptions(); options.setBinary("C:\\Program Files (x86)\\Google ...

  8. Josephus问题(Ⅲ)

    题目描述 n个人排成一圈,按顺时针方向依次编号1,2,3-n.从编号为1的人开始顺时针"一二三...."报数,报到m的人退出圈子.这样不断循环下去,圈子里的人将不断减少.最终一定会 ...

  9. 【百度飞桨】手写数字识别模型部署Paddle Inference

    从完成一个简单的『手写数字识别任务』开始,快速了解飞桨框架 API 的使用方法. 模型开发 『手写数字识别』是深度学习里的 Hello World 任务,用于对 0 ~ 9 的十类数字进行分类,即输入 ...

  10. 关于(Java)方法的再认识

    1.方法的调用(图中两个方法在一个包中) 2.静态与非静态 两个非静态可以调用 但是静态不可以同时调用非静态 package com.oop;public class Way02 { public s ...