Como concatenar cordas com eficiência

727

No Go, a stringé um tipo primitivo, o que significa que é somente leitura, e toda manipulação dele criará uma nova string.

Portanto, se eu quiser concatenar seqüências de caracteres várias vezes sem saber o tamanho da sequência resultante, qual é a melhor maneira de fazer isso?

A maneira ingênua seria:

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

mas isso não parece muito eficiente.

Randy Sugianto 'Yuku'
fonte
7
Mais um banco
Ivan Black
1
Nota: Esta pergunta e a maioria das respostas parecem ter sido escritas antes append()do idioma, o que é uma boa solução para isso. Ele terá um desempenho rápido, copy()mas aumentará a fatia primeiro, mesmo que isso signifique alocar uma nova matriz de apoio, se a capacidade não for suficiente. bytes.Bufferainda faz sentido se você deseja métodos de conveniência adicionais ou se o pacote que você está usando espera.
thomasrutter
7
Não apenas "parece muito ineficiente"; ele tem um problema específico em que todos os novos contratados que não são de CS que encontramos já enfrentam nas primeiras semanas de trabalho. É quadrático - O (n * n). Pense na sequência de números: 1 + 2 + 3 + 4 + .... É n*(n+1)/2a área de um triângulo de base n. Você aloca o tamanho 1, depois o tamanho 2, depois o tamanho 3 etc. quando anexa seqüências imutáveis ​​em um loop. Esse consumo quadrático de recursos se manifesta de mais maneiras do que apenas isso.
Rob

Respostas:

856

Nova maneira:

No Go 1.10, existe um strings.Buildertipo, consulte esta resposta para obter mais detalhes .

À moda antiga:

Use o bytespacote. Tem um Buffertipo que implementa io.Writer.

package main

import (
    "bytes"
    "fmt"
)

func main() {
    var buffer bytes.Buffer

    for i := 0; i < 1000; i++ {
        buffer.WriteString("a")
    }

    fmt.Println(buffer.String())
}

Isso é feito em O (n) tempo.

comerciante
fonte
24
em vez de println (string (buffer.Bytes ())); use poderia apenas fazer println (buffer.String ())
FigmentEngine
26
Em vez de buffer := bytes.NewBufferString(""), você pode fazer var buffer bytes.Buffer. Você também não precisa de nenhum desses pontos e vírgulas :).
crazy2be
66
Incrivelmente rápido. Fiz algumas concatenações ingênuas "+" no meu programa passar de 3 minutos para 1,3 segundos .
Malcolm
10
+1 para "O (n) tempo"; Eu acho que é importante fazer mais comentários como esse.
contradito
8
O Go 1.10 adiciona strings.Builder , que é como bytes.Buffer, mas mais rápido quando seu objetivo final é uma string.
Josh Bleecher Snyder
272

A maneira mais eficiente de concatenar seqüências de caracteres é usando a função interna copy. Nos meus testes, essa abordagem é ~ 3x mais rápida que o uso bytes.Buffere muito mais rápida (~ 12.000x) do que o operador +. Além disso, ele usa menos memória.

Eu criei um caso de teste para provar isso e aqui estão os resultados:

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

Abaixo está o código para teste:

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
fonte
6
O bytes.Buffer deve fazer basicamente o mesmo que a cópia (com alguma contabilidade extra, eu acho) e a velocidade não é tão diferente. Então, eu usaria isso :). A diferença é que o buffer começa com 0 bytes, portanto ele precisa se realocar (isso parece um pouco mais lento, eu acho). Mais fácil de usar, no entanto.
Aktau
5
buffer.Write(bytes) é 30% mais rápido que buffer.WriteString. [útil se você pode obter os dados como []byte]
Dani-Br
34
Observe que os resultados do benchmark estão distorcidos e não são autênticos. Diferentes funções de benchmark serão chamadas com valores diferentes de b.Ne, portanto, você não está comparando o tempo de execução da mesma tarefa a ser executada (por exemplo, uma função pode acrescentar 1,000strings, outra pode acrescentar, o 10,000que pode fazer uma grande diferença na média tempo de 1 anexo, BenchmarkConcat()por exemplo). Você deve usar a mesma contagem de anexos em cada caso (certamente não b.N) e fazer toda a concatenação dentro do corpo do forintervalo até b.N(ou seja, 2 forloops incorporados).
icza 4/12/15
18
Além disso, o benchmark de cópia é inclinado ignorando explicitamente o tempo que a alocação leva, o que é incluído nos outros benchmarks.
gha.st
6
Além disso, a referência de cópia depende de saber o comprimento da sequência resultante.
Skarllot
227

Em Go 1.10+ existe strings.Builder, aqui .

Um Construtor é usado para construir eficientemente uma sequência usando métodos Write. Minimiza a cópia da memória. O valor zero está pronto para uso.


Exemplo

É quase o mesmo com bytes.Buffer.

package main

import (
    "strings"
    "fmt"
)

func main() {
    // ZERO-VALUE:
    //
    // It's ready to use from the get-go.
    // You don't need to initialize it.
    var str strings.Builder

    for i := 0; i < 1000; i++ {
        str.WriteString("a")
    }

    fmt.Println(str.String())
}

Clique para ver isso no playground .


Nota

  • Não copie um valor StringBuilder, pois ele armazena em cache os dados subjacentes.
  • Se você deseja compartilhar um valor StringBuilder, use um ponteiro para ele.

Interfaces Suportadas

Os métodos do StringBuilder estão sendo implementados com as interfaces existentes em mente. Para que você possa alternar facilmente para o novo tipo de construtor em seu código.


Diferenças de bytes.Buffer

  • Só pode crescer ou redefinir.

  • Possui um mecanismo copyCheck que impede copiá-lo acidentalmente:

    func (b *Builder) copyCheck() { ... }

  • Em bytes.Buffer, pode-se acessar os bytes subjacentes como este: (*Buffer).Bytes().

    • strings.Builder evita esse problema.
    • Às vezes, isso não é um problema e é desejado.
    • Por exemplo: Para o comportamento de espreitar quando os bytes são passados ​​para um io.Readeretc.

Confira seu código-fonte para mais detalhes, aqui .

Inanc Gumus
fonte
5
O que você quer dizer com 'fuga'? Você quer dizer escapes na string ou apenas que os bytes subjacentes podem ser expostos?
makhdumi
1
@makhdumi Sim, segunda, exposição de bytes subjacentes.
Inanc Gumus 07/03/19
Vale notar que strings.Builderimplementa seus métodos usando um receptor de ponteiro, o que me impressionou por um momento. Como resultado, eu provavelmente criaria um usando new.
Duncan Jones
@DuncanJones No entanto, adicionei uma observação, como ela é usada principalmente para armazenar dados em cache, é normal usar um ponteiro para compartilhá-lo entre funções etc. Na mesma função, você pode usá-lo também como não-ponteiro.
Inanc Gumus 05/07/19
130

Há uma função de biblioteca no pacote strings chamada Join: http://golang.org/pkg/strings/#Join

Uma olhada no código de Joinmostra uma abordagem semelhante à função Anexar que Kinopiko escreveu: https://golang.org/src/strings/strings.go#L420

Uso:

import (
    "fmt";
    "strings";
)

func main() {
    s := []string{"this", "is", "a", "joined", "string\n"};
    fmt.Printf(strings.Join(s, " "));
}

$ ./test.bin
this is a joined string
mbarkhau
fonte
21
Não funciona quando você precisa fazer um loop sobre algo que não é uma string [].
Malcolm
42

Acabei de comparar a resposta principal postada acima em meu próprio código (uma caminhada recursiva na árvore) e o operador concat simples é realmente mais rápido que o 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()
}

Isso levou 0,81 segundos, enquanto o código a seguir:

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
} 

levou apenas 0,61 segundos. Provavelmente, isso se deve à sobrecarga de criação do novo BufferString.

Atualização: Também testei a joinfunção e ela foi executada em 0,54 segundos.

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,"")
}
JasonMc
fonte
5
Acredito que o OP estava mais preocupado com a complexidade da memória do que com a complexidade do tempo de execução, considerando o fato de que concatenações ingênuas de cadeias resultam em novas alocações de memória a cada vez.
Galaktor 16/08/12
15
A velocidade lenta disso pode muito bem estar relacionada ao uso do fmt. Print em vez de buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will
Fico feliz em saber que o meu método preferido de (strings.Join)corrida é o mais rápido, enquanto esse ditado (bytes.Buffer)é o vencedor!
Chetabahana 22/03
23

Você pode criar uma grande fatia de bytes e copiar os bytes das cadeias curtas usando fatias de cadeia. Existe uma função dada em "Effective Go":

func Append(slice, data[]byte) []byte {
    l := len(slice);
    if l + len(data) > cap(slice) { // reallocate
        // Allocate double what's needed, for future growth.
        newSlice := make([]byte, (l+len(data))*2);
        // Copy data (could use bytes.Copy()).
        for i, c := range slice {
            newSlice[i] = c
        }
        slice = newSlice;
    }
    slice = slice[0:l+len(data)];
    for i, c := range data {
        slice[l+i] = c
    }
    return slice;
}

Então, quando as operações forem concluídas, use string ( )a grande fatia de bytes para convertê-lo em uma sequência novamente.


fonte
É interessante que existem tantas maneiras de fazer isso no Go.
Yitzhak
11
Na prática, também diz que a idéia é tão útil que foi capturada em um prédio. Então você pode substituir sua função por append(slice, byte...), ao que parece.
precisa
23

Esta é a solução mais rápida que não requer que você saiba ou calcule primeiro o tamanho geral do buffer:

var data []byte
for i := 0; i < 1000; i++ {
    data = append(data, getShortStringFromSomewhere()...)
}
return string(data)

Pelo meu benchmark , é 20% mais lento que a solução de cópia (8,1ns por anexo em vez de 6,72ns), mas ainda 55% mais rápido que o uso de bytes.Buffer.

rog
fonte
23
package main

import (
  "fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    out := fmt.Sprintf("%s %s ",str1, str2)
    fmt.Println(out)
}
Harold Ramos
fonte
2
Bem-vindo ao Stack Overflow! Reserve um momento para ler a ajuda de edição no centro de ajuda. A formatação no estouro de pilha é diferente de outros sites.
Rizier123
2
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código. Por favor, tente também não sobrecarregar seu código com comentários explicativos, pois isso reduz a legibilidade do código e das explicações!
Rizier123
Solução simples 👍 #
Finn Finn
22

Nota adicionada em 2018

No Go 1.10, existe um strings.Buildertipo, consulte esta resposta para obter mais detalhes .

Resposta pré-201x

O código de referência de @ cd1 e outras respostas estão incorretas. b.Nnão deve ser definido na função de benchmark. É definido pela ferramenta go test dinamicamente para determinar se o tempo de execução do teste é estável.

Uma função de benchmark deve executar os mesmos b.Ntempos de teste e o teste dentro do loop deve ser o mesmo para cada iteração. Então, eu o corrigo adicionando um loop interno. Também adiciono benchmarks para outras soluções:

package main

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

const (
    sss = "xfoasneobfasieongasbg"
    cnt = 10000
)

var (
    bbb      = []byte(sss)
    expected = strings.Repeat(sss, cnt)
)

func BenchmarkCopyPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        bs := make([]byte, cnt*len(sss))
        bl := 0
        for i := 0; i < cnt; i++ {
            bl += copy(bs[bl:], sss)
        }
        result = string(bs)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppendPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, cnt*len(sss))
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferPreAllocate(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        buf := bytes.NewBuffer(make([]byte, 0, cnt*len(sss)))
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkCopy(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64) // same size as bootstrap array of bytes.Buffer
        for i := 0; i < cnt; i++ {
            off := len(data)
            if off+len(sss) > cap(data) {
                temp := make([]byte, 2*cap(data)+len(sss))
                copy(temp, data)
                data = temp
            }
            data = data[0 : off+len(sss)]
            copy(data[off:], sss)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkAppend(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        data := make([]byte, 0, 64)
        for i := 0; i < cnt; i++ {
            data = append(data, sss...)
        }
        result = string(data)
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWrite(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.Write(bbb)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkBufferWriteString(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var buf bytes.Buffer
        for i := 0; i < cnt; i++ {
            buf.WriteString(sss)
        }
        result = buf.String()
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

func BenchmarkConcat(b *testing.B) {
    var result string
    for n := 0; n < b.N; n++ {
        var str string
        for i := 0; i < cnt; i++ {
            str += sss
        }
        result = str
    }
    b.StopTimer()
    if result != expected {
        b.Errorf("unexpected result; got=%s, want=%s", string(result), expected)
    }
}

O ambiente é OS X 10.11.6, 2.2 GHz Intel Core i7

Resultado dos testes:

BenchmarkCopyPreAllocate-8         20000             84208 ns/op          425984 B/op          2 allocs/op
BenchmarkAppendPreAllocate-8       10000            102859 ns/op          425984 B/op          2 allocs/op
BenchmarkBufferPreAllocate-8       10000            166407 ns/op          426096 B/op          3 allocs/op
BenchmarkCopy-8                    10000            160923 ns/op          933152 B/op         13 allocs/op
BenchmarkAppend-8                  10000            175508 ns/op         1332096 B/op         24 allocs/op
BenchmarkBufferWrite-8             10000            239886 ns/op          933266 B/op         14 allocs/op
BenchmarkBufferWriteString-8       10000            236432 ns/op          933266 B/op         14 allocs/op
BenchmarkConcat-8                     10         105603419 ns/op        1086685168 B/op    10000 allocs/op

Conclusão:

  1. CopyPreAllocateé o caminho mais rápido; AppendPreAllocateestá bem próximo do número 1, mas é mais fácil escrever o código.
  2. Concattem um desempenho realmente ruim, tanto para velocidade quanto para uso de memória. Não use.
  3. Buffer#Writee Buffer#WriteStringsão basicamente os mesmos em velocidade, ao contrário do que @ Dani-Br disse no comentário. Considerando que stringestá de fato []byteem Go, faz sentido.
  4. bytes.Buffer basicamente usa a mesma solução que Copya manutenção extra de livros e outras coisas.
  5. Copye Appenduse um tamanho de inicialização de 64, o mesmo que bytes.
  6. Appenduse mais memória e aloque, acho que está relacionado ao algoritmo de crescimento usado. Não está aumentando a memória tão rápido quanto bytes.

Sugestão:

  1. Para tarefas simples, como o que o OP deseja, eu usaria Appendou AppendPreAllocate. É rápido o suficiente e fácil de usar.
  2. Se for necessário ler e gravar o buffer ao mesmo tempo, use, é bytes.Bufferclaro. É para isso que ele foi projetado.
PickBoy
fonte
13

Minha sugestão original foi

s12 := fmt.Sprint(s1,s2)

Mas acima de resposta usando bytes.Buffer - WriteString () é a maneira mais eficiente.

Minha sugestão inicial usa reflexão e uma opção de tipo. Veja (p *pp) doPrinte(p *pp) printArg
Não há interface universal Stringer () para tipos básicos, como eu pensava ingenuamente.

Pelo menos, Sprint () usa internamente um bytes.Buffer. portanto

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

é aceitável em termos de alocações de memória.

=> A concatenação Sprint () pode ser usada para saída rápida de depuração.
=> Caso contrário, use bytes.Buffer ... WriteString

Peter Buchmann
fonte
8
Não está embutido e não é eficiente.
PeterSO 02/07
Importar um pacote (como o fmt) significa que ele não está embutido. Está na biblioteca padrão.
Malcolm
É lento apenas porque usa reflexão sobre seus argumentos. É eficaz. Caso contrário, não é menos eficiente do que juntando com strings.Join
Ithkuil
11

Expandindo a resposta do cd1: você pode usar append () em vez de copy (). append () faz provisões antecipadas ainda maiores, custando um pouco mais de memória, mas economizando tempo. Eu adicionei mais dois benchmarks no topo do seu. Executar localmente com

go test -bench=. -benchtime=100ms

No meu thinkpad T400s, ele produz:

BenchmarkAppendEmpty    50000000         5.0 ns/op
BenchmarkAppendPrealloc 50000000         3.5 ns/op
BenchmarkCopy           20000000        10.2 ns/op
Peter Buchmann
fonte
4

Esta é a versão real do benchmark fornecida por @ cd1 ( Go 1.8, linux x86_64) com as correções de bugs mencionados por @icza e @PickBoy.

Bytes.Bufferé apenas 7mais rápido que a concatenação direta de cadeias via +operador.

package performance_test

import (
    "bytes"
    "fmt"
    "testing"
)

const (
    concatSteps = 100
)

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

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

Horários:

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op
Vitaly Isaev
fonte
Eu não acho que configurar manualmente bN é o caminho certo para funções uso de referência do pacote de teste
PickBoy
@PickBoy, justifique seu ponto de vista. Por que você acha que b.Né uma variável pública?
precisa saber é o seguinte
1
bN não deve ser definido na função de benchmark. É definido pela ferramenta de teste dinamicamente. Uma função de benchmark deve executar o mesmo teste bN vezes, mas no seu código (assim como no código do @ cd1), todo teste no loop é um teste diferente (porque o comprimento da string está aumentando)
PickBoy
@PickBoy, se você deixar a ferramenta de teste ir b.Ndinamicamente, você terminará com uma sequência de diferentes comprimentos em diferentes casos de teste. Veja o comentário
Vitaly Isaev
É por isso que você deve adicionar um loop interno de um número fixo de iterações, como 10000, dentro do loop bN.
PickBoy 27/04
3

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()
}
Xian Shu
fonte
1

Eu faço isso usando o seguinte: -

package main

import (
    "fmt"
    "strings"
)

func main (){
    concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator. 
    fmt.Println(concatenation) //abc
}
Krish Bhanushali
fonte
Isso não resolve o problema do OP de criar uma cadeia de caracteres através de uma série de iterações, com um loop for.
codeforester
1
package main

import (
"fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    result := make([]byte, 0)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)
    result = append(result, []byte(str1)...)
    result = append(result, []byte(str2)...)

    fmt.Println(string(result))
}
rajni kant
fonte
3
Por favor, não publique apenas respostas do código. Por favor, explique o que esse código faz e por que é a solução.
Korashen 9/08/19
-1

resultado de benchmark com estatísticas de alocação de memória. verifique o código de referência no github .

use strings.Builder para otimizar o desempenho.

go test -bench . -benchmem
goos: darwin
goarch: amd64
pkg: github.com/hechen0/goexp/exps
BenchmarkConcat-8                1000000             60213 ns/op          503992 B/op          1 allocs/op
BenchmarkBuffer-8               100000000               11.3 ns/op             2 B/op          0 allocs/op
BenchmarkCopy-8                 300000000                4.76 ns/op            0 B/op          0 allocs/op
BenchmarkStringBuilder-8        1000000000               4.14 ns/op            6 B/op          0 allocs/op
PASS
ok      github.com/hechen0/goexp/exps   70.071s
hechen0
fonte
dê crédito a @ cd1 pelos casos de teste originais em que você está desenvolvendo aqui.
colm.anseo 20/02/19
-2
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))
user2288856
fonte
5
Esta é uma solução muito lenta, porque usa reflexão, analisa a cadeia de formatação e faz uma cópia dos dados para a []byte(s1)conversão. Comparando-o com outras soluções postadas, você pode citar uma única vantagem da sua solução?
pts
-5

strings.Join() do pacote "strings"

Se você tem uma incompatibilidade de tipo (como se você estiver tentando ingressar em um int e uma string), você faz RANDOMTYPE (coisa que deseja alterar)

EX:

package main

import (
    "fmt"
    "strings"
)

var intEX = 0
var stringEX = "hello all you "
var stringEX2 = "people in here"


func main() {
    s := []string{stringEX, stringEX2}
    fmt.Println(strings.Join(s, ""))
}

Resultado :

hello all you people in here
liam
fonte
4
Esse código nem compila: strings.Join()usa apenas 2 parâmetros: uma fatia e um separador string.
icza 29/08/16
isso não pode ajudar
Anshu
adicione algumas alterações aqui.
Anshu