7 切片slice
include
- 切片
- 切片的日常用法
切片slice
- 其本身并不是数组,它指向底层的数组
- 作为变长数组的替代方案,可以关联底层数组的局部或者
- 为引用类型。
- 可以直接创建或从底层数组获取生成。
- 使用len()获取元素个数,cap()获取容量
- 一般使用make()创建
- 如果多个slice指向相同底层数组,其中一个的值改变会影响全部。
- make([]T,len,cap)
- 其中cap可以省略,则和Len的值相同。
- len()表示存数的元素个数,cap表示容量
最简单的切片:
先看看如何切片,我们先定义一个数组,然后开始切片,切片我们是根据输入的下标来取值的。
切片的包含关系是,包左不包右,什么意思呢?就是在做切片处理的时候,右边结束的下标是不取,只取左边开始的下标到右边结束下标的前一位值。
func main() {
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(a)
s1 := a[5:7]
fmt.Println(s1)
}
切片常用方法:
- 从某一位下标取到最后一位下标
- 从第一位下标到指定的某一位下标
func main() {
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(a)
s1 := a[5:7]
s2 := a[6:len(a)] // 从某一位下标取到最后一位下标
s3 := a[5:] // 从某一位下标取到最后一位下标
s4 := a[:5] // 从第一位下标到指定的某一位下标
fmt.Println(s1, s2, s3, s4)
}
通过切片获取这个数组的所有值
我们可以通过切片获取这个数组的所有值
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := s1[:]
fmt.Println(s1, s2)
}
打印的结果如下:
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := s1[:]
fmt.Println(s1, s2)
}
make 制作切片
make 语法要点:
make(数组类型,包含多少个元素,容量)
举个例子,比如你这个数组的长度没有超过容量,那么就不需额外重新分配内存,
如超过了这个长度,那么就需要重新分配一倍的内存地址给她,比如初试容量为10,
那么下次分配容量就是20了。
如果容量没有设置的话,那么就默认使用元素的个数作为当前的容量。
func main() {
s1 := make([]int, 3, 10) // int类型数组,包含多少个元素,容量。
/* 举个例子,比如你这个数组的长度没有超过容量,那么就不需额外重新分配内存,
如超过了这个长度,那么就需要重新分配一倍的内存地址给她,比如初试容量为10,
那么下次分配容量就是20了。
如果容量没有设置的话,那么就默认使用元素的个数作为当前的容量 */
fmt.Println(s1, cap(s1), len(s1))
}
s2 := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g'}
sa := s2[2:5]
sb := sa[3:5] // 此时取sa[3:5]分片等于以s2的下标为2的元素开始
fmt.Println(string(sb), string(sa))
Reslice
- Reslice时索引以被slice的切片为准
- 索引不可以超过slice的切片的容量cap()值
- 索引越界不会导致底层数组重新分配而是引发错误,也就是切片不了了。
slice与底层数组的对应关系
每次切片得出来的新数组,其实这个数组的容量就是从这个切片的开始位置到被切片数组最后一位,什么意思呢?看下面的代码讲解
func main() {
s2 := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'g'}
sa := s2[2:5]
fmt.Println(cap(sa), len(sa)) // 此时sa的容量是8,8就是从下标为2开始的元素到末尾,总共8个元素
sb := sa[3:5] // 此时取sa[3:5]分片等于以s2的下标为2的元素开始
fmt.Println(string(sb), string(sa))
}
修改切片出来的数组同时会影响其他切片数组
a := []int{1, 2, 3, 4, 5}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(s1, s2)
s1[0] = 9
fmt.Println(s1, s2)
打印的结果如下:
[3 4 5] [2 3]
[9 4 5] [2 9]
显然结果很明显,我们在修改s1的数组的时候,也修改了原始数组,同时,也影响到了对这个原始数组切片的其他数组。但是对于append,就不会这样了。因为append会返回一个新的数组,是不受影响的。
Append
- 可以在slice尾部追加元素
- 可以将一个slice追加在另一个slice尾部
- 如果最终长度没有超过追加到slice的容量则返回原始slice
- 如果超过追加到的slice的容量,则重新分配数组并拷贝原始数据。
代码例子:
func main() {
a := []int{1, 2, 3, 4, 5}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(a, s1, s2)
s2 = append(s2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
s1[0] = 9
fmt.Println(a, s1, s2)
}
我们看下打印结果:
[1 2 9 4 5] [9 4 5] [2 3 1 2 3 4 5 6 7 8 9 10]
由打印结果可得:s2不受影响,因为这是append出来的一个新的数组。
Copy
- 长度短的数组 copy 长度长的数组
func main() {
s1 := []int{1, 2, 3, 4}
s2 := []int{7, 8, 9}
copy(s1, s2) // s1是copy到的切片,s2被copy的数组,s2的长度为3,那么s1的前三位就会被从s2 copy过来的值给覆盖掉
fmt.Println(s1, s2)
}
打印结果如下:
[7 8 9 4] [7 8 9]
很显然,那你可以发现s1的前三位就会被从s2 copy过来的值给覆盖掉.
- 长度长的数组 copy 长度短的数组
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := []int{7, 8, 9}
copy(s2, s1)
fmt.Println(s1, s2)
}
打印结果如下:
[1 2 3 4 5 6] [1 2 3]
很显然,你可发现s2的长度依旧是三位,并不能把s1完完整整的复制过去。
- copy的高级用法-指定数组下标进行copy
在copy的时候,我们可以使用copy去指定数组下标来copy,代码例子如下:
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := []int{7, 8, 9, 11, 12, 13}
copy(s2[4:], s1[2:4])
fmt.Println(s1, s2)
}
如何打印某个数组的所有元素
我们有下面三个方法请看代码:
func main() {
s1 := []int{1, 2, 3, 4, 5}
s2 := s1[:] // 方法1
s3 := s1[0:] // 方法2
s4 := s1[:len(s1)] // 方法3
fmt.Println(s2, s3, s4)
}