背景
go在内置中有 append ,使用 append 和 copy 可以模仿使用 container/vector 包里面的大部分功能。
下述的内容来源是 golang wiki
下面是向量方法和它们的切片操作类似方法
AppendVector
添加向量列表的数据
a = append(a, b...)
Copy
复制;切片的复制,避免指针的引用问题。
b = make([]T, len(a))
copy(b, a)
// These two are often a little slower than the above one,
// but they would be more efficient if there are more
// elements to be appended to b after copying.
b = append([]T(nil), a...)
b = append(a[:0:0], a...)
Cut
剪切;用于移除部分的数据
a = append(a[:i], a[j:]...)
例如用于移除下标i的数据
a = append(a[:i], a[i+1:]...)
Delete
删除
a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]
删除的技巧可 Cut的类似
Delete without preserving order
不确保顺序的删除
a[i] = a[len(a)-1]
a = a[:len(a)-1]
将切片最后一位下标的值赋值到i下标中
然后删除最后一位的数据;确保下标i的数据被替换删除
注意 如果元素是一个指针的类型或结构体指针字段,需要垃圾收集,上面的实现削减和删除一个潜在的内存泄漏问题:一些元素值仍引用的部分,因此不能被收集。 下面的代码可以修复这个问题:
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]
把 j 后面的数据复制到 i 下标后面
对需要剪切的数据 赋值为 nil 进行处理
重新切片处理;移除多余的nil引用
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]
上述的避免内存泄露的问题的解决方案,只要是把对应的指针指向nil。从而减少对象的引用,在gc扫描的时候,可以扫描到被剔除的对应。因此而避免内存泄露。
Expand
切片扩大
a = append(a[:i], append(make([]T, j), a[i:]...)...)
在下标i 中间 扩大 j 的切片数据
Extend
a = append(a, make([]T, j)...)
在尾部新增j的容量空间的切片
Filter (in place)
在适当位置的拦截器
n := 0
for _, x := range a {
if keep(x) {
a[n] = x
n++
}
}
a = a[:n]
Insert
插入
a = append(a[:i], append([]T{x}, a[i:]...)...)
注意:第二个append使用自己的底层存储创建一个新的slice,并将a[i:]中的元素复制到该slice中,然后将这些元素复制回slice a(通过第一个append)。 可以使用另外的方便来避免创建新的分片和元素
Insert
s = append(s, 0 /* use the zero value of the element type */)
copy(s[i+1:], s[i:])
s[i] = x
InsertVector
a = append(a[:i], append(b, a[i:]...)...)
注意:为了获得最佳效率,最好不使用append进行插入操作,特别是当插入元素的数量已知时
// Assume element type is int.
func Insert(s []int, k int, vs ...int) []int {
if n := len(s) + len(vs); n <= cap(s) {
s2 := s[:n]
copy(s2[k+len(vs):], s[k:])
copy(s2[k:], vs)
return s2
}
s2 := make([]int, len(s) + len(vs))
copy(s2, s[:k])
copy(s2[k:], vs)
copy(s2[k+len(vs):], s[k:])
return s2
}
a = Insert(a, i, b...)
Push
a = append(a, x)
Pop
x, a = a[len(a)-1], a[:len(a)-1]
选取最后一个元素
移除最后一个元素
Push Front/Unshift
在开头插入元素
a = append([]T{x}, a...)
Pop Front/Shift
移除首位元素
x, a = a[0], a[1:]
Additional Tricks 额外的技巧
Filtering without allocating
这个技巧利用了一个切片与原始切片共享相同的后备数组和容量这一事实,因此存储被过滤后的切片重用。当然,原始内容会被修改。
b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x)
}
}
对于必须被垃圾回收的元素,可以随后包含以下代码:
for i := len(b); i < len(a); i++ {
a[i] = nil // or the zero value of T
}
Reversing 反转
将一个切片的内容替换为相同的元素,但顺序相反:
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]
}
Shuffling 洗牌
把一个数组乱序处理
for i := len(a) - 1; i > 0; i-- {
j := rand.Intn(i + 1)
a[i], a[j] = a[j], a[i]
}
Batching with minimal allocation 使用最小分配进行批处理
如果您想对大型片进行批处理,这很有用。 分片处理,把一个大分片切割成小的分片。
actions := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
batchSize := 3
batches := make([][]int, 0, (len(actions) + batchSize - 1) / batchSize)
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]]
In-place deduplicate (comparable)
合并去重处理 ;剔除重复的数据
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++
// preserve the original data
// in[i], in[j] = in[j], in[i]
// only set what is required
in[j] = in[i]
}
result := in[:j+1]
fmt.Println(result) // [1 2 3 4]
Move to front, or prepend if not present, in place if possible.
移动到前面,如果不存在,则在适当的位置新增元素。
// moveToFront moves needle to the front of haystack, in place if possible.
func moveToFront(needle string, haystack []string) []string {
if len(haystack) != 0 && haystack[0] == needle {
return haystack
}
prev := needle
for i, elem := range haystack {
switch {
case i == 0:
haystack[0] = needle
prev = elem
case elem == needle:
haystack[i] = prev
return haystack
default:
haystack[i] = prev
prev = elem
}
}
return append(haystack, prev)
}
haystack := []string{"a", "b", "c", "d", "e"} // [a b c d e]
haystack = moveToFront("c", haystack) // [c a b d e]
haystack = moveToFront("f", haystack) // [f c a b d e]
Sliding Window 滑动窗口
类似于窗口分片处理
func slidingWindow(size int, input []int) [][]int {
// returns the input slice as the first element
if len(input) <= size {
return [][]int{input}
}
// allocate slice at the precise size we need
r := make([][]int, 0, len(input)-size+1)
for i, j := 0, size; j <= len(input); i, j = i+1, j+1 {
r = append(r, input[i:j])
}
return r
}
结果如下
result := slidingWindow(3, []int{1, 2, 3, 4,5,6,7,8})
fmt.Println(result) //[[1 2 3] [2 3 4] [3 4 5] [4 5 6] [5 6 7] [6 7 8]]
总结
擅用 slice 切片 以及避免 slice引用导致的bug。