切片类型( slice)
最后更新于:2022-04-02 02:36:06
[TOC]
## 切片类型( slice)
1.切片的创建方式 基于底层数组创建,直接创建,或者 make() 函数创建
1. 与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。会进行分配内存和 copy 操作
1. len() 方法获取长度。
3. 添加切片 `append` 与拷贝切片 `copy`
```
//创建
a :=[]int {10,20,30,40,50,60,70}
b :=a[1:3] //{20,30}
c :=a[1:] //{20,30,40,50,60,70}
d :=a[:3] //{10,20,30}
//添加贴片
var numbers =[]int{1,2,3}
/* 允许追加空切片 */
numbers = append(numbers, 3)
fmt.Println(numbers) //[1 2 3 3]
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers) //len=3 cap=8 slice=[0 1 2]
```
> 区别
```
a :=[]int{1,2,3} //切片
b :=[...]int{1,2,3} //数组
c :=b[:] //从数组中生成切片
fmt.Println(reflect.TypeOf(a))//[]int
fmt.Println(reflect.TypeOf(b)) //[3]int
fmt.Println(reflect.TypeOf(c)) //[]int -切片 值为[1 2 3]
```
切片传值
> [参考文章](https://studygolang.com/articles/4810)
```
func main() {
args :=make([]string,0)
args=append(args,"1")
demo(args)
fmt.Printf("%+v\n", args) // [1]
demo1(args)
fmt.Printf("%+v\n", args) // [-1]
}
// 在切片中追加,无效
func demo(args []string){
args=append(args,"2")
}
// 可修改已有切片的值
func demo1(args []string){
args[0]="-1"
args=nil // 赋值nil 也无效
}
```
### make([]int) 指定的长度根据真实长度扩展
```
a := make([]int, 2, 3)
fmt.Println(len(a)) //2
fmt.Println(cap(a)) //3
a = append(a, 2, 2, 2)
fmt.Println(len(a)) //5
fmt.Println(cap(a)) //6
```
### 切片内存技巧
返回值可以为切片的复用
```
func TrimSpace(s []byte) []byte {
b := s[:0]
for _, x := range s {
if x != ' ' {
b = append(b, x)
}
}
return b
}
```
### 避免切片内存泄漏
切片操作并不会复制底层的数据。底层的数组会被保存在内存中,直到它不再被引用。但是有时候可能会因为一个小的内存引用而导致底层整个数组处于被使用的状态,这会延迟自动内存回收器对底层数组的回收
bad
```
func FindPhoneNumber(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
return regexp.MustCompile("[0-9]+").Find(b)
}
```
good:
```
func FindPhoneNumber(filename string) []byte {
b, _ := ioutil.ReadFile(filename)
b = regexp.MustCompile("[0-9]+").Find(b)
return append([]byte{}, b...)
}
```
## 操作
### copy
```
a := []int{1, 2, 3, 4}
b := make([]int, len(a))
copy(b, a)
fmt.Printf("%+v\n", b) // [1 2 3 4]
```
### Append
```
a=append(a,b...)
```
* 当 append 之后的长度小于等于 cap,将会直接利用原底层数组剩余的空间。
* 当 append 后的长度大于 cap 时,则会分配一块更大的区域来容纳新的底层数组。
> 因此,为了避免内存发生拷贝,如果能够知道最终的切片的大小,预先设置 cap 的值能够获得最好的性能。
### Delete
切片的底层是数组,因此删除意味着后面的元素需要逐个向前移位
方式一
```
a := []int{1, 2, 3, 4}
index := 2 // 删除第三个
a = append(a[:index], a[index+1:]...)
fmt.Printf("%+v\n", a) // [1,2,4]
```
方式二:
```
a := []int{1, 2, 3, 4}
index := 2 // 删除第三个
a = a[:index+copy(a[index:], a[index+1:])]
fmt.Printf("%+v\n", a) // [1,2,4]
```
### Delete(GC)
删除后,将空余的位置置空,有助于垃圾回收
```
a := []int{1, 2, 3, 4}
index := 2 // 删除第三个
copy(a[index:], a[index+1:])
a[len(a)-1] = 0 // 置空 zero or nil
a = a[:len(a)-1]
fmt.Printf("%+v\n", a) // [1,2,4]
```
### Insert
```
a := []int{1, 2, 3, 4}
index := 2 // 删除第三个
a = append(a[:index], append([]int{9}, a[index+1:]...)...)
fmt.Printf("%+v\n", a) // [1,2,9,4]
```
即在某个位置添加一个元素后,将该位置后面的元素再 append 回去。复杂度为 O(N)。因此,不适合大量随机插入的场景
### Push
在末尾追加元素,不考虑内存拷贝的情况,复杂度为 O(1)
```
a=append(a,x)
```
在头部追加元素,时间和空间复杂度均为 O(N),不推荐。
```
a := []int{1, 2, 3, 4}
a = append([]int{9}, a...)
fmt.Printf("%+v\n", a) // [9 1 2 3 4]
```
### Pop
尾部删除
```
a = a[:len(a)-1]
```
头部删除
```
a = a[1:]
```
';