一文看懂 slice
Go 语言中虽然有数组,但在代码中直接用的比较少,而是会间接的用到,slice 存储数据就是用的 数组,甚至可以认为数组是为了 slice 存在。Go 语言中的 slice 可以当做数组来使用,也可以当做其他语言中的 List 来使用。
slice 表示一个拥有相同类型元素的可变长的序列。可变长是 slice 最重要的一个特性,这是它和数组最不同的地方,既然 slice 是依靠数组而存在的,那么 slice 是如何做到可变长的呢,这篇文章就来聊一下。
先从 slice 的创建说起。
1. slice 的创建
slice 可以主动创建,也可从现有的数组中获取。
带初始化元素的方式:
不带初始化元素的方式,下面的这种方式会自动填充零值:
还可以创建一个更大容量的 slice:
在这里需要需要注意,虽然 slice 容量有 12,但是却不能直接访问 slice 长度范围外的元素,否则会出现 panic:
要么通过扩展 slice 的长度,要么使用 append 函数来添加元素。
在使用上面的方式创建 slice 时,Go 会自动的创建一个底层数组来存储数据,slice 本身并不会存储数据。
还有一种方式是通过数组来创建 slice:
2. slice 的结构
一般 可以把 slice 看成是一个复合结构,结构如下,由三部分组成:指针、长度和容量:
slice 本身不会存储任何数据,slice 通过指针指向底层数组的某一个位置,这个位置就是 slice 的初始位置。长度表示当前 slice 中的元素个数,容量表示从指针的位置到底层数组的最后一个位置。
slice 可变长的第一个原因就是可以通过改变指针的位置来改变 slice 的长度。比如下面这样:
这个操作在 numsSlice 的基础上创建的一个新的 slice,其实就是移动了一下 slice 的指针。
另外 slice 不能使用 == 进行比较,因为 slice 的元素是非直接的,也就是 slice 本身不存储值,slice 甚至可以包含自身。另外因为 slice 的元素是靠底层的数组存储,所以当底层数组变动的时候,slice 读取到的值也会产生变化。如果 使用 == 来进行比较,可能会产生很多预料之外的结果。
3. 理解 slice 的 cap
对于初学者来说,会把 slice 的长度和容量搞混。
先来看下面这段代码:
我们可以发现,同样是从数组上来生成 slice,但是生成之后的 slice 的容量却不一样,在上面而我们说到了 slice 的初始位置靠指针来决定,s1 指针的位置在数组的最开始的位置。按照容量的定义,从指针到底层数组结束的地方,所以它的 容量还是 6。
而 s2 中,指针的位置是数组的第四个元素,长度是 3,容量也是 3。这是初学者最容易犯错的地方。
长度表示 slice 中目前可访问的元素个数,容量则表示这个 slice 在不改变底层数组的情况下,最多个扩展到的长度。如果要扩展 slice,使用下面的操作就可以:
这种方式是让 slice 可变长的第二种方式。
4. 理解 append
append 方法也是让 slice 可变长的一种方式。
append 函数经常会配合 slice 一起使用,在使用 append 的时候,我们需要使用下面的语法:
而且这种做法是必须的,如果没有采用这种做法,可能会产生意料之外的结果。看下面的代码:
上面的代码在调用 append 之后,如果没有使用返回的 slice,而是直接使用原来的 slice,就会产生越界的错误。
因为 slice 本身虽然是可变长的,所以如果 slice 还有容量,那么每次添加元素,都需要扩展 slice 的长度,返回的也是一个新的 slice。
另外 slice 依赖的底层数组是固定长度,在使用 append 时,如果底层的数组不足以存储新的元素之后,就需要扩容,扩容之后就会产生一个新的 slice 返回。
可以理解 append 方法修改传入的 slice,而我们在调用 append 函数之后,就需要使用函数返回的结果。所以上面的代码需要修改为:
5. 小结
slice 是一个很有用的数据接口,既可以当数组用,也可以当 list 使用。在使用 slice 的过程中,一定要注意 slice 本身不存储数据,它只是在一个底层数组上,截取不同长度序列,可以说 slice 就是一个数组的子序列。
另外我们说到了 slice 可变长的几种方式,一种是通过移动 slice 的指针,改变 slice 的长度,第二种是 slice 容量范围内扩展长度,第三种是通过 append 方式,这种方式会创建一个新的 slice。
另外我们经常会对 string 做子串的截取操作和这里 slice 工作原理是一样的。
文 / Rayjun
本文首发于微信公众号【Rayjun】
版权声明: 本文为 InfoQ 作者【Rayjun】的原创文章。
原文链接:【http://xie.infoq.cn/article/5199b218610e1e5a18b7ea63e】。文章转载请联系作者。
评论