原文出自SliceTricks,by Eugene Russkikh.
由于内建函数中引入了append方法,在包container/vector中很多方法在Go 1.0版本中被移除,但可以用append和copy方法替换。
下面是vector的方法和对应的分片操作。
追加Vector (Append Vector)
a = append(a, b...)
复制(Copy)
b = make([]T, len(a))
copy(b, a)
// or
b = append([]T(nil), a...)
// or
b = append(a[:0:0], a...) // See https://github.com/go101/go101/wiki
译者注:关于a[:0:0]被称为Three-Index Slices,是在Go 1.2版本中引入的,更多请参考这里.
切分(Cut)
a = append(a[:i], a[j:]...)
删除(Delete)
a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]
删除,但不保留原来顺序(Delete without preserving order)
a[i] = a[len(a)-1]
a = a[:len(a)-1]
注意如果元素的类型是一个指针,或者是一个包含指针字段的结构体,是需要被垃圾回收的。上面cut、delete的实现存在潜在的内存泄漏的问题:一些元素的值可能仍然被a一直引用而不被释放。下面的代码可以解决这个问题。
切分(Cut)
copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]
删除(Delete)
copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]
删除,但不保留原来顺序(Delete without preserving order)
a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]
展开(Expand)
a = append(a[:i], append(make([]T, j), a[i:]...)...)
扩展(Extend)
a = append(a, make([]T, j)...)
插入(Insert)
a = append(a[:i], append([]T{x}, a[i:]...)...)
注意第二个append在他自己的空间下创建了一个新的分片并将元素拷贝到a[1:]中,然后再将这些元素拷贝回分片a(通过第一个append)。创建分片(会进行内存分配)和第二个拷贝操作能够通过另一种方法避免:
插入(Insert)
s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x
插入Vector(Insert Vector)
a = append(a[:i], append(b, a[i:]...)...)
弹出/左移(Pop/Shift)
x, a = a[0], a[1:]
弹出队尾元素(Pop Back)
x, a = a[len(a)-1], a[:len(a)-1]
压入(Push)
a = append(a, x)
压入头部/末移动(Push Front/Unshift)
a = append([]T{x}, a...)
其它技巧
无需要分配空间的过滤器
该技巧是利用分片是共享原始的底层数据存储和容量,所以过滤分片的时候存储可以被再利用,当然,原始数据内容也被修改了。
b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x)
}
}
反转
将分片中的容量反转:
for i := len(a)/2-1; i >= 0; i-- {
opp := len(a)-1-i
a[i], a[opp] = a[opp], a[i]
}
相同效果,但额外使用了两个的索引:
for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
a[left], a[right] = a[right], a[left]
}
随机
Fisher-Yates 算法:
从Go 1.10开始,可以在 math/rand.Shuffle中查看。
for i := len(a) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
a[i], a[j] = a[j], a[i]
}
最小空间使用下的批量操作
当需要对超大分片进行批量处理时非常有用。
actions := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
batchSize := 3
var batches [][]int
for batchSize < len(actions) {
actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize])
}
batches = append(batches, actions)
结果为:
[[0 1 2] [3 4 5] [6 7 8] [9]]
就地去重
import "sort"
in := []int{3,2,1,4,3,2,1,4,1} // any item can be sorted
sort.Ints(in)
j := 0
for i := 1; i < len(in); i++ {
if in[j] == in[i] {
continue
}
j++
in[i], in[j] = in[j], in[i]
}
fmt.Println(in[:j+1]) // [1 2 3 4]
网友评论