请参阅this playground。如您所见,我在结构中有一个切片。我还有一个方法可用于向切片添加新元素。这很好。
但现在我的问题是我想扩展该方法,以便它留下切片的n
个元素。因此,在添加新元素时,最老的"应该删除,并添加新的。
我该怎么做? Aren有没有我可以使用的开箱即用的包裹?
答案 0 :(得分:1)
如果要从切片s
中删除第一个元素(那里已经存在的元素最长的元素),您可以简单地执行s = s[1:]
,从第一个开始对切片进行sa引用旧切片的元素。
我修改了你的代码来执行此操作:
https://play.golang.org/p/Eu-KLoinz0
package main
import (
"fmt"
"time"
)
type Statistics struct {
LastScan time.Time
Imports []Import
}
type Import struct {
text string
}
func (s *Statistics) AddImport(i Import) {
s.Imports = append(s.Imports, i)
const max = 2
if len(s.Imports) > max {
s.Imports = s.Imports[len(s.Imports)-max:]
}
}
func main() {
s := Statistics{}
s.AddImport(Import{text: "myText1"})
fmt.Println(s.Imports)
s.AddImport(Import{text: "myText2"})
fmt.Println(s.Imports)
s.AddImport(Import{text: "myText3"})
fmt.Println(s.Imports)
s.AddImport(Import{text: "myText4"})
fmt.Println(s.Imports)
}
答案 1 :(得分:1)
子片。语法slice[n:m]
将输入切片的部分从n返回到m-1。可以省略任何一个分别暗示0或len(切片)。所以slice[n:]
表示"给我从n
到结尾的切片部分。 slice[len(slice)-n:]
将为您提供切片中的最后n
个条目。
https://play.golang.org/p/4JRcRH-wc3
func (s *Statistics) AddImport(i Import) {
// How can I optimize this method so that
// only the last two entries are kept?
s.Imports = append(s.Imports, i)
if len(s.Imports) > numberToKeep {
s.Imports = s.Imports[len(s.Imports)-numberToKeep:]
}
}
请注意,这不会从内存中删除切片的开头(或允许它被垃圾回收),但是当您继续添加条目时,运行时将自动分配更大的底层数组并复制内容,释放早期数组分配是垃圾收集。它目前在2倍的基础上执行此操作(因此每次切片的大小增加时都会这样做),尽管这没有记录,因此无法保证保持这种状态。如果内存管理对您很重要,您可以使用内置的copy(dest, source)
函数手动将切片复制到新的后备阵列。
高级选项包括使用队列系统(通常使用链接列表格式实现)或圆形切片(其中条目"包围"从末尾到数组开头的未使用索引,另一种常见的队列设计)。
答案 2 :(得分:1)
例如,
addimport.go
:
package main
import (
"fmt"
"time"
)
type Statistics struct {
LastScan time.Time
Imports []Import
}
type Import struct {
text string
}
func (s *Statistics) AddImport(i Import) {
// only the last n entries are kept
const n = 2 // n > 0 and small
if len(s.Imports) >= n {
copy(s.Imports, s.Imports[len(s.Imports)-n+1:])
s.Imports = s.Imports[:n-1]
}
s.Imports = append(s.Imports, i)
}
func main() {
s := Statistics{}
fmt.Println(len(s.Imports), cap(s.Imports), s.Imports)
s.AddImport(Import{text: "myText1"})
s.AddImport(Import{text: "myText2"})
s.AddImport(Import{text: "myText3"})
fmt.Println(len(s.Imports), cap(s.Imports), s.Imports)
}
游乐场:https://play.golang.org/p/204-uB8Zls
输出:
0 0 []
2 2 [{myText2} {myText3}]
代码应该合理有效。 Go有一个基准测试包。以下是peterSO,Kaedys和gonutz解决方案的基准测试结果。
$ go test -bench=. addimport_test.go
BenchmarkAddImport/PeterSO-4 100000 16145 ns/op 96 B/op 3 allocs/op
BenchmarkAddImport/Kaedys-4 30000 59344 ns/op 32032 B/op 502 allocs/op
BenchmarkAddImport/Gonutz-4 30000 60447 ns/op 32032 B/op 502 allocs/op
addimport_test.go
:
package main
import (
"testing"
"time"
)
type Statistics struct {
LastScan time.Time
Imports []Import
}
type Import struct {
text string
}
func (s *Statistics) AddImportPeterSO(i Import) {
// only the last n entries are kept
const n = 2 // n > 0 and small
if len(s.Imports) >= n {
copy(s.Imports, s.Imports[len(s.Imports)-n+1:])
s.Imports = s.Imports[:n-1]
}
s.Imports = append(s.Imports, i)
}
const numberToKeep = 2
func (s *Statistics) AddImportKaedys(i Import) {
s.Imports = append(s.Imports, i)
if len(s.Imports) > numberToKeep {
s.Imports = s.Imports[len(s.Imports)-numberToKeep:]
}
}
func (s *Statistics) AddImportGonutz(i Import) {
s.Imports = append(s.Imports, i)
const max = 2
if len(s.Imports) > max {
s.Imports = s.Imports[1:]
}
}
func benchmarkAddImport(b *testing.B, addImport func(*Statistics, Import)) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
var s Statistics
for j := 0; j < 1000; j++ {
addImport(&s, Import{})
}
}
}
func BenchmarkAddImport(b *testing.B) {
b.Run("PeterSO", func(b *testing.B) {
benchmarkAddImport(b, (*Statistics).AddImportPeterSO)
})
b.Run("Kaedys", func(b *testing.B) {
benchmarkAddImport(b, (*Statistics).AddImportKaedys)
})
b.Run("Gonutz", func(b *testing.B) {
benchmarkAddImport(b, (*Statistics).AddImportGonutz)
})
}
游乐场:https://play.golang.org/p/Q2X_T5Vofe
此问题的一般形式是循环缓冲区:Circular buffer。