切片Slice
一 切片介绍
切片(Slice)顾名思义就是“把东西拿过来切一刀,得到一片,哈哈哈,话糙理不糙,只是注意一下此处的切一刀并不是切走了哈”。
我们可以对数组、切片、指向数组或切片的指针、字符串进行切片操作,得到的是一个切片Slice或子字符串。
简单切片操作:s[i:j]
完整切片操作:s[i:j:max]
切片Slice类型其实是一个元素类型相同且长度可变的序列。切片和数组有着紧密的联系,
-
1、与数组相同的是:
-
2、与数组不同的是
切片由三个部分构成:指针、长度、容量
二 创建切片
2.1 对数组进行切片操作得到切片类型
多个slice之间可以共享底层的数据,例如上述Q2和summer,并且引用的数组部分区间可能重叠,如下图所示
2.2 对切片进行切片操作得到切片类型
2.3 对指向数组或切片的指针进行切片操作来得到切片类型
2.4 对字符串进行切片操作得到子字符串
字符串的切片操作和[]byte字节类型切片的切片操作是类似的。它们都写作x[m:n],并且都是返回一个原始字节序列的子序列,底层都是共享之前的底层数组,因此切片操作对应常量时间复杂度。x[m:n]切片操作对于字符串则生成一个新字符串,如果x是[]byte的话则生成一个新的[]byte。
2.5 make创建切片类型,指向一个隐式的底层数组
我们可以直接声明一个切片类型,但是它的零值为nil,一个零值的切片等于nil,nil代表空、没有分配内存,所以说一个nil值的切片并没有底层数组
为了防止运行时申请内存带来的效率问题,我们可以用make函数为切片事先申请好了内存空间,内置的make函数创建一个指定元素类型
、长度
和容量
的slice。容量部分可以省略,在这种情况下,容量将等于长度
在底层,make创建了一个匿名的数组变量,然后返回一个slice;只有通过返回的slice才能引用底层匿名的数组变量
在第一种语句中,创建了一个元素个数为len个的底层数组,然后得到的切片类型引用了整个数组,切片的长度等于切片的容量,这是切片操作能达到的最大长度了
在第二个语句中,创建了一个元素个数为cap个的底层数组,然后只引用了底层数组的前len个元素,若len<cap,那额外的元素就是留给未来的增长用的。
2.6 声明并初始化切片,指向一个隐式的底层数组
slice和数组的字面值语法很类似,它们都是用花括弧包含一系列的初始化元素。
但是对于slice并没有指明序列的长度,这会隐式地创建一个合适大小的数组,然后slice的指针指向底层的数组。
像数组字面值一样,slice的字面值可以按顺序指定初始化值序列如上所示
也可以通过索引和元素值指定,或者的两种风格的混合语法初始化,如下所示
三 切片不能直接进行相等性比较
切片与数组还有一点不同的就是,slice彼此之间不能比较,因为切片是引用类型,它所包含的真实值都来自于或者说间接引用于底层数组中的元素,而我们想比较的都是值而不是引用,比较引用也没有意义,因为底层数组的元素是可以随时被修改的,
当然,我们自己取出切片中的元素逐一进行比较是可以的,如下所示
因为我们可以声明一个切片类型,它的零值为nil,如下所示
所以说事实上切片slice唯一合法的比较操作就是和nil比较,例如:
综上:如果你需要测试一个slice是否是空的,使用len(s) == 0来判断,而不应该用s == nil来判断。
除了和nil相等比较外,一个nil值的slice的行为和其它任意0产长度的slice一样;例如reverse(nil)也是安全的。除了文档已经明确说明的地方,所有的Go语言函数应该以相同的方式对待nil值的slice和0长度的slice。
四 传递切片修改关联数组
因为数组是值类型,传入函数后,是一个新的副本,在函数内修改并不会影响原数组,若想修改,那么必须传递数组的引用,我们可以传递数组指针