在Go中,字符串是一种基本类型,这意味着它是只读的,对它的每次操作都会创建一个新字符串。

如果我想多次连接字符串而不知道结果字符串的长度,最好的方法是什么?

最天真的做法是:

var s string
for i := 0; i < 1000; i++ {
    s += getShortStringFromSomewhere()
}
return s

但这似乎不是很有效。


当前回答

我只是在我自己的代码(递归树遍历)中对上面发布的顶部答案进行了基准测试,简单的concat操作符实际上比BufferString更快。

func (r *record) String() string {
    buffer := bytes.NewBufferString("");
    fmt.Fprint(buffer,"(",r.name,"[")
    for i := 0; i < len(r.subs); i++ {
        fmt.Fprint(buffer,"\t",r.subs[i])
    }
    fmt.Fprint(buffer,"]",r.size,")\n")
    return buffer.String()
}

这花了0.81秒,而下面的代码:

func (r *record) String() string {
    s := "(\"" + r.name + "\" ["
    for i := 0; i < len(r.subs); i++ {
        s += r.subs[i].String()
    }
    s += "] " + strconv.FormatInt(r.size,10) + ")\n"
    return s
} 

只花了0.61秒。这可能是由于创建新BufferString的开销。

更新:我还对连接函数进行了基准测试,它在0.54秒内运行。

func (r *record) String() string {
    var parts []string
    parts = append(parts, "(\"", r.name, "\" [" )
    for i := 0; i < len(r.subs); i++ {
        parts = append(parts, r.subs[i].String())
    }
    parts = append(parts, strconv.FormatInt(r.size,10), ")\n")
    return strings.Join(parts,"")
}

其他回答

goutils。JoinBetween

 func JoinBetween(in []string, separator string, startIndex, endIndex int) string {
    if in == nil {
        return ""
    }

    noOfItems := endIndex - startIndex

    if noOfItems <= 0 {
        return EMPTY
    }

    var builder strings.Builder

    for i := startIndex; i < endIndex; i++ {
        if i > startIndex {
            builder.WriteString(separator)
        }
        builder.WriteString(in[i])
    }
    return builder.String()
}

我最初的建议是

s12 := fmt.Sprint(s1,s2)

但以上答案使用字节。Buffer - WriteString()是最有效的方法。

我最初的建议是使用反射和类型开关。参见(p *pp) doPrint和(p *pp) printArg 我曾经天真地认为,基本类型没有通用的Stringer()接口。

至少Sprint()内部使用bytes.Buffer。因此

`s12 := fmt.Sprint(s1,s2,s3,s4,...,s1000)`

在内存分配方面是可接受的。

Sprint()连接可用于快速调试输出。 =>否则使用bytes。缓冲……WriteString

如果你知道你要预分配的字符串的总长度,那么最有效的连接字符串的方法可能是使用内置函数拷贝。如果你事先不知道总长度,不要抄写,而是阅读其他答案。

在我的测试中,这种方法比使用字节快3倍。Buffer,并且比使用运算符+快得多(~ 12000倍)。此外,它使用更少的内存。

我创建了一个测试用例来证明这一点,结果如下:

BenchmarkConcat  1000000    64497 ns/op   502018 B/op   0 allocs/op
BenchmarkBuffer  100000000  15.5  ns/op   2 B/op        0 allocs/op
BenchmarkCopy    500000000  5.39  ns/op   0 B/op        0 allocs/op

下面是测试代码:

package main

import (
    "bytes"
    "strings"
    "testing"
)

func BenchmarkConcat(b *testing.B) {
    var str string
    for n := 0; n < b.N; n++ {
        str += "x"
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); str != s {
        b.Errorf("unexpected result; got=%s, want=%s", str, s)
    }
}

func BenchmarkBuffer(b *testing.B) {
    var buffer bytes.Buffer
    for n := 0; n < b.N; n++ {
        buffer.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); buffer.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", buffer.String(), s)
    }
}

func BenchmarkCopy(b *testing.B) {
    bs := make([]byte, b.N)
    bl := 0

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        bl += copy(bs[bl:], "x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); string(bs) != s {
        b.Errorf("unexpected result; got=%s, want=%s", string(bs), s)
    }
}

// Go 1.10
func BenchmarkStringBuilder(b *testing.B) {
    var strBuilder strings.Builder

    b.ResetTimer()
    for n := 0; n < b.N; n++ {
        strBuilder.WriteString("x")
    }
    b.StopTimer()

    if s := strings.Repeat("x", b.N); strBuilder.String() != s {
        b.Errorf("unexpected result; got=%s, want=%s", strBuilder.String(), s)
    }
}

扩展cd1的答案: 你可以使用append()而不是copy()。 Append()提供了更大的预先准备,占用了更多的内存,但节省了时间。 我在你的上面又加了两个基准测试。 在本地运行

go test -bench=. -benchtime=100ms

在我的thinkpad T400s上,它产生:

BenchmarkAppendEmpty    50000000         5.0 ns/op
BenchmarkAppendPrealloc 50000000         3.5 ns/op
BenchmarkCopy           20000000        10.2 ns/op

我使用以下方法:-

package main

import (
    "fmt"
    "strings"
)

func main (){
    concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator. 
    fmt.Println(concatenation) //abc
}