写点什么

秒懂 Go 数组和切片的内部实现原理

2019 年 9 月 24 日

秒懂Go数组和切片的内部实现原理

很多人对 Go 语言的 array 和 slice 傻傻分不清楚,今天我们就从底层出发,来聊聊它俩到底有什么区别。


数组

几乎所有计算机语言,数组的实现都是相似的:一段连续的内存,Go 语言也一样,Go 语言的数组底层实现就是一段连续的内存空间。每个元素有唯一一个索引(或者叫下标)来访问。如下图所示,下图是[5]int{1:10, 2:20}数组的内部实现逻辑图:



由于内存连续,CPU 很容易计算索引(即数组的下标),可以快速迭代数组里的所有元素。


Go 语言的数组不同于 C 语言或者其他语言的数组,C 语言的数组变量是指向数组第一个元素的指针;而 Go 语言的数组是一个值,Go 语言中的数组是值类型,一个数组变量就表示着整个数组,意味着 Go 语言的数组在传递的时候,传递的是原数组的拷贝。你可以理解为 Go 语言的数组是一种有序的 struct


slice

切片是一个很小的对象,是对数组进行了抽象,并提供相关的操作方法。切片有三个属性字段:长度、容量和指向数组的指针。



上图中,ptr 指的是指向 array 的 pointer,len 是指切片的长度, cap 指的是切片的容量。现在,我想你对数组和切片有了一个本质的认识。


切片有多种声明方式,每种初始化方式对应的逻辑图是怎样的呢?

1)对于 s := make([]byte, 5)和 s := []byte{…}的方式



2)对于 s = s[2:4]的方式



3)对于 nil 的切片即 var s []byte 对应的逻辑图



在此有一个说明:nil 切片和空切片是不太一样的,空切片即 s := make([]byte, 0)或者 s := []byte{}出来的切片


空切片的逻辑图为:



空切片指针不为 nil,而 nil 切片指针为 nil。但是,不管是空切片还是 nil 切片,对其调用内置函数 append()、len 和 cap 的效果都是一样的,感受不到任何区别。


扩容

slice 这种数据结构便于使用和管理数据集合,可以理解为是一种“动态数组”,slice 也是围绕动态数组的概念来构建的。既然是动态数组,那么 slice 是如何扩容的呢?


请记住以下两条规则:


1)如果切片的容量小于 1024 个元素,那么扩容的时候 slice 的 cap 就翻番,乘以 2;一旦元素个数超过 1024 个元素,增长因子就变成 1.25,即每次增加原来容量的四分之一。


2)如果扩容之后,还没有触及原数组的容量,那么,切片中的指针指向的位置,就还是原数组,如果扩容之后,超过了原数组的容量,那么,Go 就会开辟一块新的内存,把原来的值拷贝过来,这种情况丝毫不会影响到原数组。


知道了一下规则,请看下面程序,试问输出结果:


 1import ( 2    "fmt" 3) 4func main(){ 5    array := [4]int{10, 20, 30, 40} 6    slice := array[0:2] 7    newSlice := append(append(append(slice, 50), 100), 150) 8    newSlice[1] += 1 9    fmt.Println(slice)10}
复制代码


输出:


[10 20]
复制代码


答对了吗?


作者介绍:


作者沙加(企业代号名),目前负责贝壳找房运维开发方向的相关工作。


本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。


原文链接:


https://mp.weixin.qq.com/s/r6Z7Zk-OHirmGRXXwNQbYw


2019 年 9 月 24 日 14:32300

评论 1 条评论

发布
用户头像
demo是想说明发生扩容之后新slice是copy 不是一个引用
2020 年 10 月 16 日 11:50
回复
没有更多了
发现更多内容

一位男程序员的英语学习之路

盛安德软件

【Kafka】消费者客户端小结(java)

guoguo 👻

英特尔神经拟态芯片Loihi大显身手 帮助轮椅上的儿童实现独立生活

最新动态

linux入门系列7--管道符、重定向、环境变量

黑马腾云

Linux centos 运维 linux命令 管道符

第11周总结

娄江国

物联网SIM卡和SIM卡真的不是一回事

华为云开发者社区

人工智能 物联网 华为云 传感器 SIM卡

如何让我的简历有价值、有亮点

escray

学习 面试 简历 面试现场

质量门禁:Verigreen开启Git的Commit门禁

陈磊@Criss

可能是首个支持部署 Deno 前后端应用的部署工具

binggg

taro GitHub 前端 deno Node

网页游戏

小端taro

影响音视频延迟的关键因素(二): 采集、前处理、编解码

ZEGO即构

H264 API 3A算法

终极学习法,你能学会任何东西--程序员的学习之路

盛安德软件

区块链技术助力甘肃建食安信息追溯平台 为食品安全“立规矩”

CECBC区块链专委会

食品追溯 食品安全

全票通过!易观开源项目DolphinScheduler进入Apache孵化器

易观大数据

区块链技术正向平台化、组件化、集成化演进

CECBC区块链专委会

大数据 区块链技术 科技

INT类型知多少

Simon

MySQL

linux入门系列8--shell编程入门

黑马腾云

Linux centos Shell linux命令 linux编程

37岁程序员被裁,想用6月工资跪舔领导划掉被裁名额,结果蒙了!

程序员生活志

提高GIT中代码质量的七点优秀实践

程序员生活志

git 经验总结

​JDK1.8新特性(八):还在重复写空指针检查代码?赶紧使用Optional吧!​

xcbeyond

Java 新特性 JDK1.8 Optional

第11周作业

娄江国

开源,轻松实现RTC与SIP互通

anyRTC开发者

WebRTC 编码 SIP 源码解析

linux入门系列9--用户管理及文件权限控制

黑马腾云

Linux centos centos7 linux运维 linux用户权限

火眼云CEO张陆鹏:A轮融资5000万,解密国内ABM生态首位玩家

ToB行业头条

王者荣耀为什么不使用微服务架构?

程序员生活志

非IT行业大企程序员讲述MIS系统开发案例

Learun

敏捷开发 企业信息化 企业管理 .net core 「Java 25周年」

CHAR与VARCHAR详解

Simon

MySQL

python自动生成一整月的排班表

openbytes

Python

linux入门系列6--软件管理之rpm和yum仓库

黑马腾云

Linux centos 运维 rpm yum

Devops与敏捷二者能否结合?

禅道项目管理

DevOps Scrum 敏捷开发

企业信息化怎么构建?

代码制造者

大数据 低代码 企业信息化 零代码 编程开发

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

秒懂Go数组和切片的内部实现原理-InfoQ