美文网首页
【翻译】Go 分片技巧

【翻译】Go 分片技巧

作者: AlgoPeek | 来源:发表于2018-10-11 00:08 被阅读0次

原文出自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]

相关文章

网友评论

      本文标题:【翻译】Go 分片技巧

      本文链接:https://www.haomeiwen.com/subject/suqvaftx.html