[TOC]
### 实现
* 切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对底层数组的抽象。
* 因为基于数组实现,所以它的底层的内存是连续分配的,效率非常高,还可以通过索引获得数据,可以迭代以及垃圾回收优化。
* 切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用底层数组,设定相关属性将数据读写操作限定在指定的区域内。
* 切片本身是一个只读对象,其工作机制类似数组指针的一种封装。
### 结构
切片对象非常小,是因为它是只有3个字段的数据结构:所以即使数据再多,大小也是固定的
* 指向底层数组的指针
* 切片的长度
* 切片的容量
![](https://img.kancloud.cn/01/6c/016c6e325510b0a8cfc6a7d3b6ddb8fb_881x280.png)
### 扩容机制
* 首先判断,如果新申请容量大于 2 倍的旧容量,最终容量就是新申请的容量。
* 否则判断,如果旧切片的长度小于 1024,则最终容量就是旧容量的两倍。
* 否则判断,如果旧切片长度大于等于 1024,则最终容量从旧容量开始循环增加原来的 1/4 , 直到最终容量大于等于新申请的容量。
* 如果最终容量计算值溢出,则最终容量就是新申请容量。
(对于 append 向 slice 添加元素时,假如 slice 容量够用,则追加新元素进去,slice.len++,返回原来的 slice。当原容量不够,则 slice 先扩容,扩容之后 slice 得到新的 slice,将元素追加进新的 slice,slice.len++,返回新的 slice。)
情况一:原数组还有容量可以扩容(实际容量没有填充完),这种情况下,扩容以后的数组还是指向原来的数组,对一个切片的操作可能影响多个指针指向相同地址的Slice。
情况二:原来数组的容量已经达到了最大值,再想扩容, Go 默认会先开一片内存区域,把原来的值拷贝过来,然后再执行 append() 操作。这种情况丝毫不影响原数组。
要复制一个Slice,最好使用Copy函数。
### eg
再次强调 `切片的本质就是对底层数组的封装`,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)。
eg:现在有一个数组`a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}`,切片`s1 := a[:5]`,相应示意图如下。
![](https://img.kancloud.cn/f9/6b/f96b82a5e7e400d9de90022d1fab0fad_1280x567.png)
切片`s2 := a[3:6]`,相应示意图如下:
![](https://img.kancloud.cn/86/1b/861b4ef69eea429996c366c1176e9fe3_1278x536.png)
### 判断切片是否为空
要检查切片是否为空,要用`len(s) == 0`来判断,而不应该使用`s == nil`来判断。
- Go准备工作
- 依赖管理
- Go基础
- 1、变量和常量
- 2、基本数据类型
- 3、运算符
- 4、流程控制
- 5、数组
- 数组声明和初始化
- 遍历
- 数组是值类型
- 6、切片
- 定义
- slice其他内容
- 7、map
- 8、函数
- 函数基础
- 函数进阶
- 9、指针
- 10、结构体
- 类型别名和自定义类型
- 结构体
- 11、接口
- 12、反射
- 13、并发
- 14、网络编程
- 15、单元测试
- Go常用库/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go优化
- Go问题排查
- Go框架
- 基础知识点的思考
- 面试题
- 八股文
- 操作系统
- 整理一份资料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小点
- 树
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面试题
- 基础
- Map
- Chan
- GC
- GMP
- 并发
- 内存
- 算法
- docker