使用切片值的Golang字符串格式
在这里,我尝试从包含字符串的切片为我的API创建查询字符串.
即.其中= { “的node_name”: “节点1”,“节点名称”: “node_2”} import ( "fmt" "strings" ) func main() { nodes := []string{"node1","node2"} var query string for _,n := range nodes { query += fmt.Sprintf(""node_name":"%s",",n) } query = strings.TrimRight(query,") final := fmt.Sprintf("where={%s}",query) fmt.Println(final) } 这是goplayground链接. 获得结果的最佳方法是什么?
由于字符串连接,您的解决方案使用了太多的分配.
我们将创建一些替代的,更快的和/或更优雅的解决方案.请注意,下面的解决方案不会检查节点值是否包含引号“character.如果愿意,那些必须以某种方式进行转义(否则结果将是无效的查询字符串). 完整的可运行代码可以在Go Playground上找到.完整的测试/基准测试代码也可以在Go Playground上找到,但它不可运行,只保存到Go工作区(例如$GOPATH / src / query / query.go和$GOPATH / src / query / query_test.go)并使用go test -bench运行它. 另请务必查看以下相关问题:How to efficiently concatenate strings in Go? 备择方案 创世纪 您的逻辑可以通过以下函数捕获: func buildOriginal(nodes []string) string { var query string for _,") return fmt.Sprintf("where={%s}",query) } 使用bytes.Buffer 更好的是使用单个缓冲区,例如 func buildBuffer(nodes []string) string { buf := &bytes.Buffer{} buf.WriteString("where={") for i,v := range nodes { if i > 0 { buf.WriteByte(',') } buf.WriteString(`"node_name":"`) buf.WriteString(v) buf.WriteByte('"') } buf.WriteByte('}') return buf.String() } 使用它: nodes := []string{"node1","node2"} fmt.Println(buildBuffer(nodes)) 输出: where={"node_name":"node1","node_name":"node2"} bytes.Buffer改进了 bytes.Buffer仍会进行一些重新分配,尽管比原始解决方案要少得多. 但是,如果我们在使用 func buildBuffer2(nodes []string) string { size := 8 + len(nodes)*15 for _,v := range nodes { size += len(v) } buf := bytes.NewBuffer(make([]byte,size)) buf.WriteString("where={") for i,') } buf.WriteString(`"node_name":"`) buf.WriteString(v) buf.WriteByte('"') } buf.WriteByte('}') return buf.String() } 请注意,在大小计算中,8是字符串的大小,其中= {},15是字符串“node_name”的大小:“”,. 使用文本/模板 我们还可以创建一个文本模板,并使用 var t = template.Must(template.New("").Parse(templ)) func buildTemplate(nodes []string) string { size := 8 + len(nodes)*15 for _,size)) if err := t.Execute(buf,nodes); err != nil { log.Fatal(err) // Handle error } return buf.String() } const templ = `where={ {{- range $idx,$n := . -}} {{if ne $idx 0}},{{end}}"node_name":"{{$n}}" {{- end -}} }` 使用strings.Join() 由于其简单性,该解决方案很有趣.我们可以使用 需要注意的一点是:strings.Join()使用内置的 func buildJoin(nodes []string) string { if len(nodes) == 0 { return "where={}" } return `where={"node_name":"` + strings.Join(nodes,`","node_name":"`) + `"}` } 基准测试结果 我们将使用以下节点值进行基准测试: var nodes = []string{"n1","node2","nodethree","fourthNode","n1",} 基准测试代码如下所示: func BenchmarkOriginal(b *testing.B) { for i := 0; i < b.N; i++ { buildOriginal(nodes) } } func BenchmarkBuffer(b *testing.B) { for i := 0; i < b.N; i++ { buildBuffer(nodes) } } // ... All the other benchmarking functions look the same 现在的结果是: BenchmarkOriginal-4 200000 10572 ns/op BenchmarkBuffer-4 500000 2914 ns/op BenchmarkBuffer2-4 1000000 2024 ns/op BenchmarkBufferTemplate-4 30000 77634 ns/op BenchmarkJoin-4 2000000 830 ns/op 一些不足为奇的事实:buildBuffer()比buildOriginal()快3.6倍,而buildBuffer2()(预先计算的大小)比buildBuffer()快30%,因为它不需要重新分配(并复制)内部缓冲. 一些令人惊讶的事实:buildJoin()非常快,甚至比buildBuffer2()快2.4倍(由于只使用了[]字节和copy()).另一方面,buildTemplate()证明非常慢:比buildOriginal()慢7倍.造成这种情况的主要原因是它在引擎盖下使用(必须使用)反射. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |