Go 语言入门 3-动态数组(slice)的特性及实现原理
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)的特性及实现原理的更多相关文章
- (待续)C#语言中的动态数组(ArrayList)模拟常用页面置换算法(FIFO、LRU、Optimal)
目录 00 简介 01 算法概述 02 公用方法与变量解释 03 先进先出置换算法(FIFO) 04 最近最久未使用(LRU)算法 05 最佳置换算法(OPT) 00 简介 页面置换算法主要是记录内存 ...
- C语言基础 - 实现动态数组并增加内存管理
用C语言实现一个动态数组,并对外暴露出对数组的增.删.改.查函数 (可以存储任意类型的元素并实现内存管理) 这里我的编译器就是xcode 分析: 模拟存放 一个 People类 有2个属性 字符串类型 ...
- C语言实现使用动态数组实现循环队列
我在上一篇博客<C语言实现使用静态数组实现循环队列>中实现了使用静态数组来模拟队列的操作. 因为数组的大小已经被指定.无法动态的扩展. 所以在这篇博客中,我换成动态数组来实现. 动态数组能 ...
- C语言实现使用动态数组来构造栈结构
我在面前一篇博客<C语言实现使用静态数组来构造栈结构>中使用了静态数组来模拟栈的操作.静态数组的大小是在代码中写死的.是存储在用户栈上面的,使用起来不灵活.在这篇博客中我会使用动态数组来构 ...
- 纯C语言(C89)实现动态数组
起因 工作很少接触纯C项目,业余写着玩玩,不断雕琢 目标 纯C实现动态数组,提供方便易用泛型接口,避免依赖 实现 完全封装,隐藏结构体细节,不支持栈创建 拷贝存储,轻微性能代价换来易用性 vector ...
- 算法入门 - 基于动态数组的栈和队列(Java版本)
之前我们学习了动态数组的实现,接下来我们用它来实现两种数据结构--栈和队列.首先,我们先来看一下栈. 什么是栈? 栈是计算机的一种数据结构,它可以临时存储数据.那么它跟数组有何区别呢? 我们知道,在数 ...
- 使用java语言实现一个动态数组(详解)(数据结构)
废话不多说,上代码 1.从类名开始(我真是太贴心了) public class Array<E> 首先数组类需要带有泛型,这个不多说.需要注意的是在java中,数组只能存放同一个类型的. ...
- C语言入门:一维数组的概要
数组的概念: 具有相同数据的有序集合 一维数组的定义格式: int a[5]; 类型说明符 数组名(标识符)[常量表达式(长度)]; 一维数组下标 : 数组的下标 从0开始 最大下标值 为 数组的 ...
- C++ Primer : 第十二章 : 动态内存之动态数组
动态数组的分配和释放 new和数组 C++语言和标准库提供了一次分配一个对象数组的方法,定义了另一种new表达式语法.我们需要在类型名后跟一对方括号,在其中指明要分配的对象的数目. int* arr ...
随机推荐
- 29.MySQL高级SQL语句
MySQL高级SQL语句 目录 MySQL高级SQL语句 创建两个表 SELECT DISTINCT WHERE AND OR IN BETWEEN 通配符 LIKE ORDER BY 函数 数学函数 ...
- 从位图到布隆过滤器,C#实现
前言 本文将以 C# 语言来实现一个简单的布隆过滤器,为简化说明,设计得很简单,仅供学习使用. 感谢@时总百忙之中的指导. 布隆过滤器简介 布隆过滤器(Bloom filter)是一种特殊的 Hash ...
- StringJoiner的使用
1.添加字符串 add()方法 StringJoiner sj = new StringJoiner(","); sj.add("我爱你"); sj.add(& ...
- c# 国内外ORM 框架 dapper efcore sqlsugar freesql hisql sqlserver数据常规插入测试性能对比
c# 国内外ORM 框架 dapper efcore sqlsugar freesql hisql sqlserver数据常规插入测试性能对比对比 在6.22 号发布了 c# sqlsugar,his ...
- Visual Studio 生产环境配置方案:SlowCheetah
原文 SlowCheetah 能满足你不同编译模式产生不同 app.config 配置的需求,已被微软纳入麾下,支持XML,JSON格式. 下面我们用XML的格式来试试如何使用SlowCheetah, ...
- ansible概述、安装、模块介绍
一.Ansible介绍 Ansible是一 个基于Python开发的配置管理和应用部署工具,现在也在自动化管理领域大放异彩. 它融合了众多老牌运维工具的优点,Pubbet和Saltstack能实现的功 ...
- 报告指SpaceX估值已达到1000亿美元,埃隆马斯克以此回击其接受政府补贴的批判
SpaceX首席执行官埃隆-马斯克(Elon Musk)表示,名下的航天发射服务供应商市值已达到1000亿美元.该金额是根据上个月的评估报告确认的,标志着SpaceX在完成最新一轮融资,并筹集超过10 ...
- gotoscan:CMS指纹识别工具
gotoscan 前言 项目地址 https://github.com/newbe3three/gotoscan 结合自己学习到的Go相关知识,通过实现这个简易的CMS指纹识别工具来锻炼一下自己写代码 ...
- dockerfile操作
简介 Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明. 上下文路径 如下是一个简单的基于dockerfile创建镜像的命令,我们注意到启动处理镜像名和 ...
- HTML js 复习
<a href="#top" target="_self">返回顶部</a> 返回页面顶部代码 打印js对象方法 function wr ...