Golang官方总结: Slice Tricks
由于引入了内建的append
的方法, 包container/vector
的很多方法都被移除,可以被内建的append
和copy
方法代替。
下面是栈vector的操作方法的实现,使用slice实现相关的操作。
1. Append Vector
2. Copy
1
2
3
4
|
b = make([]T, len(a))
copy(b, a)
//如果a不为空, 等效实现
b = append([]T(nil), a...)
|
3. Cut
1
|
a = append(a[:i], a[j:]...)
|
4. Delete
1
2
3
|
a = append(a[:i], a[i + 1]...)
// 或者
a = a[:i + copy(a[i:], a[i + 1])]
|
5. Delete,而不保持原有顺序
1
2
|
a[i] = a[len(a) - 1]
a = a[:len(a) - 1]
|
注意:如果需要被GC回收的元素是一个指针,或者struct含有指针字段,上面的cut,delete实现可能就导致内存泄漏:一些元素的值会被a一直引用而不会被回收。下面的实现可以解决这个问题:
Cut
1
2
3
4
5
|
copy(a[i:], a[j:])
for k,n := len(a)-j+i, len(a); k<n; k++ {
a[k] = nil
}
a = a[:len(a)-j+i]
|
Delete
1
2
3
|
copy(a[i:], a[i+1:])
a[len(a)-1] = nil
a = a[:len(a)-1]
|
Delete 而不保持原来的顺序
1
2
3
|
a[i] =a[len(a)-1]
a[len(a)-1]=nil
a = a[:len(a)-1]
|
6.Expand
1
|
a = append(a[:i], append(make([]T, j), a[i:]...)...)
|
7.Extend
1
|
a = append(a, make([]T, j)...)
|
8.Insert
1
|
a = append(a[:i], append([]T{x}, a[i:]...)...)
|
注意 : 第二个append使用自己的底层存储创建一个新的slice,然后复制a[:i]中的元素到这个slice中,然后再把这些元素复制回a。新slice的创建和第二次的复制通过另外一种方式避免:
1
2
3
|
s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x
|
9. Insert Vector
1
|
a = append(a[:i], append(b, a[i:]...)...)
|
10. Pop
1
|
x, a = a[len(a)-1], a[:len(a)-1]
|
11. Push
12. Shift
13. Unshift
1
|
a = append([]T{x}, a...)
|
14.其他技巧
无新内存分配的过滤
这个技巧利用是: slice共享底层的array和存储容量。所以,在过滤slice时,会重用底层的存储。与此同时,底层存储的数据必然会被修改。
1
2
3
4
5
6
|
b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x)
}
}
|
反转
使用相同的元素(不分配新的对象)替换slice中内容,并将其反序。
1
2
3
4
|
for i:= len(a)/2 - 1; i >= 0; i--{
opp := len(a)-1-i
a[i], a[opp] = a[opp], a[i]
}
|
下面的代码实现同样的效果,只不过使用了两个索引变量
1
2
3
|
for left, right := 0, len(a)-1; left < right; left, right = left + 1, right -1 {
a[left] , r[right] = a[right], a[left]
}
|