Go数组&切片使用

2021/11/1 实践总结Go

# 声明

数组:

var array [size] string
1

切片:

var slice [] string
1

差异:数组的声明需要指定“size”,如果不指定就是为切片(slice)

# 初始化

数组:

var array1 = [3] string{"one", "two", "three"}
var array2 = [...] string{"one", "two", "three"}
array3 := [...] string{"one", "two", "three"} // 推荐:无需通过var声明
1
2
3

切片:

var slice [] string
slice = make([]string, 5, 5) // 指定长度为5,容量为5的切片
slice = make([]string5) // 指定长度为5,容量为5的切片
slice2 := [] string{"one", "two", "three"} // 推荐:无需通过var声明
1
2
3
4

注:大部分情况下如果不知道切片长度,只要声明,无需初始化,单需要往切片增加数据是,通过append添加(声明的切面默认值是nil,但是append并不会异常)

# 常用操作

# 读取

数组

数组一般是在for循环中通过索引,或是range 直接读取:

aa := [5] string{}
for i, a := range aa { // aa的副本
   fmt.Println("i= ", i, ", s=", a)
}
for i:=0; i< len(aa); i++ {
   fmt.Println("i= ", i, ", s=", aa[i])
}
1
2
3
4
5
6
7

注意:

range 的是数组的副本,也是就是说会把aa拷贝一份,并且读取出来的项也是对应的副本,所以对项的修改是不会影响原有的数组,当aa比较大的时候会影响性能,所以可以采用读取数组指针的方式来优化:

for i, a := range &aa { // 这里拷贝的会是aa的指针
   fmt.Println("i= ", i, ", s=", a)
}
1
2
3

切片

切片的读取和数组类似一般是在for循环中通过索引,或是range 直接读取:

aa := [] string{“hello”, "world"}
for i, a := range aa { // aa副本,但是由于切片时特殊的结构体,并不会保存整个数组,而是数据组第一个元素的地址,所以就算副本对性能也不会有太大影响
   fmt.Println("i= ", i, ", s=", a)
}
for i:=0; i< len(aa); i++ {
   fmt.Println("i= ", i, ", s=", aa[i])
}
1
2
3
4
5
6
7

注意:

切片和数组存在一些差异,切片range的时候拷贝的是slice的数据,底层还是共享数组,所以修改的时候回会影响原来slice

# 添加

数组:

一个数组中的元素个数总是恒定的,我们无法向其中添加元素,也无法从其中删除元素。但是元素可以通过索引修改值

aa := [5] string{}
aa[0] = "张三"
1
2

切片:

切片的底层实际上也是关联一个数组,所以如果新增元素超过数组的容量,内部会重新开辟一个数组大于(小于1024翻倍,大于增加25%)当前的数组

头部插入

aa := [] string{"张三", "李四"}
result := append([]string{"王五"}, aa...) // result = {"王五", "张三", "李四"}
1
2

尾部追加

aa := [] string{"张三", "李四"}
result := append(aa, "王五", "张飞") // 可以追加多个,result = { "张三", "李四", "王五", "张飞"}
1
2

# 删除

删除第i个元素

// 第一种方法(保持剩余元素的次序):
s = append(s[:i], s[i+1:]...)
// 第二种方法(保持剩余元素的次序):
s = s[:i + copy(s[i:], s[i+1:])]
// 上面两种方法都需要复制len(s)-i-1个元素。
// 第三种方法(不保持剩余元素的次序):
s[i] = s[len(s)-1]
s = s[:len(s)-1]
1
2
3
4
5
6
7
8

条件删除:keep判断条件,需要保留的返回true

func filter(data [] *T, keep func(item *T) bool, clear bool) [] *T{
    result:=data[:0] // 复用data内存,无需另外开辟。(注:会修改原切片共享数组内容)
    for _, d := range data {
        if keep(d) {
            result = append(result, d)
        }
    }
    if clear { // 避免暂时性的内存泄露。
        temp := data[len(result):]
        for i := range temp {
            temp[i] = nil // t0是类型T的零值
        }
    }
    return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 拷贝

拷贝b切片

方法1

var b [] T
if a!=nil{
  b = make([]T, len(a))
  copy(b, a)
}
1
2
3
4
5

方法2

var b [] T
if a!=nil{
  b = append([]T(nil), a...)
}
1
2
3
4

方法3(最简洁)

b = append(a[:0:0], a...)
1

这里就利用了当a为nil的是时候,a[:0:0]并不会报错,仍然会是nil且a[:0:0] == nil为true