Come concatenare in modo efficiente le stringhe in corso


727

In Go, a stringè un tipo primitivo, il che significa che è di sola lettura e ogni sua manipolazione creerà una nuova stringa.

Quindi, se voglio concatenare le stringhe molte volte senza conoscere la lunghezza della stringa risultante, qual è il modo migliore per farlo?

Il modo ingenuo sarebbe:

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

ma questo non sembra molto efficiente.


7
Un'altra panchina
Ivan Black il

1
Nota: questa domanda e la maggior parte delle risposte sembrano essere state scritte prima che append()entrasse nella lingua, il che è una buona soluzione per questo. Funzionerà rapidamente come copy()ma aumenterà prima la sezione anche se ciò significa allocare un nuovo array di supporto se la capacità non è sufficiente. bytes.Bufferha ancora senso se vuoi i suoi metodi di convenienza aggiuntivi o se il pacchetto che stai utilizzando lo prevede.
thomasrutter

7
Non "sembra molto inefficiente"; ha un problema specifico che ogni nuovo noleggio non CS che abbiamo mai incontrato si imbatte nelle prime settimane di lavoro. È quadratico - O (n * n). Pensate al numero di sequenza di: 1 + 2 + 3 + 4 + .... È n*(n+1)/2l'area di un triangolo di base n. Allocare la dimensione 1, quindi la dimensione 2, quindi la dimensione 3, ecc. Quando si aggiungono stringhe immutabili in un ciclo. Questo consumo quadratico di risorse si manifesta in molti modi diversi da questo.
Rob,

Risposte:


856

Nuovo modo:

Da Go 1.10 c'è un strings.Buildertipo, per favore dai un'occhiata a questa risposta per maggiori dettagli .

Vecchio modo:

Usa il bytespacchetto. Ha un Buffertipo che 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())
}

Questo lo fa in O (n) tempo.


24
invece di println (string (buffer.Bytes ())); use potrebbe fare semplicemente println (buffer.String ())
FigmentEngine,

26
Invece di buffer := bytes.NewBufferString(""), puoi farlo var buffer bytes.Buffer. Inoltre non hai bisogno di nessuno di quei punti e virgola :).
crazy2be

66
Incredibilmente veloce. Ho fatto un po 'di ingenuo concatenamento di stringhe "+" nel mio programma da 3 minuti a 1,3 secondi .
Malcolm,

10
+1 per "O (n) tempo"; Penso che sia importante fare altre osservazioni come questa.
contraddetto

8
Go 1.10 aggiunge strings.Builder , che è come bytes.Buffer ma più veloce quando il vostro obiettivo finale è una stringa.
Josh Bleecher Snyder il

272

Il modo più efficiente per concatenare le stringhe è utilizzare la funzione integrata copy. Nei miei test, questo approccio è ~ 3 volte più veloce dell'uso bytes.Buffere molto più veloce (~ 12.000 volte) rispetto all'utilizzo dell'operatore +. Inoltre, utilizza meno memoria.

Ho creato un caso di prova per dimostrarlo e qui ci sono i risultati:

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

Di seguito è riportato il codice per il test:

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)
    }
}

6
Il byte.Buffer dovrebbe fare sostanzialmente lo stesso della copia (con un po 'di contabilità extra immagino) e la velocità non è così diversa. Quindi lo userei :). La differenza è che il buffer inizia con 0 byte, quindi deve riallocare (questo fa sembrare un po 'più lento, immagino). Più facile da usare, però.
Aktau,

5
buffer.Write(byte) è più veloce del 30% rispetto a buffer.WriteString. [utile se riesci a ottenere i dati come []byte]
Dani-Br

34
Si noti che i risultati del benchmark sono distorti e non autentici. Verranno chiamate funzioni di benchmark diverse con valori diversi di b.N, quindi non si sta confrontando il tempo di esecuzione della stessa attività da svolgere (ad esempio una funzione potrebbe aggiungere 1,000stringhe, un'altra potrebbe aggiungere 10,000che può fare una grande differenza nella media tempo di 1 append, BenchmarkConcat()ad esempio). Dovresti usare lo stesso conteggio di append in ogni caso (certamente no b.N) e fare tutta la concatenazione all'interno del corpo dell'intervallo forche va b.N(ovvero, 2 forloop incorporati).
Icza,

18
Inoltre, il benchmark della copia è distorto ignorando esplicitamente il tempo impiegato dall'allocazione, che è incluso negli altri benchmark.
gha.st,

6
Inoltre, il benchmark della copia si basa sulla conoscenza della lunghezza della stringa risultante.
Skarllot

227

In Go 1.10+ c'è strings.Builder, qui .

Un builder viene utilizzato per creare in modo efficiente una stringa utilizzando i metodi di scrittura. Riduce al minimo la copia della memoria. Il valore zero è pronto per l'uso.


Esempio

È quasi lo stesso con 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())
}

Clicca per vedere questo nel parco giochi .


Nota

  • Non copiare un valore StringBuilder poiché memorizza nella cache i dati sottostanti.
  • Se si desidera condividere un valore StringBuilder, utilizzare un puntatore ad esso.

Interfacce supportate

I metodi di StringBuilder vengono implementati tenendo conto delle interfacce esistenti. In questo modo puoi passare facilmente al nuovo tipo di Builder nel tuo codice.


Differenze dai byte. Buffer

  • Può solo crescere o ripristinare.

  • Ha un meccanismo incorporato di CopyCheck che impedisce la copia accidentale:

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

  • In bytes.Buffer, si può accedere ai bytes sottostanti come questo: (*Buffer).Bytes().

    • strings.Builder previene questo problema.
    • A volte, questo non è un problema e invece lo si desidera.
    • Ad esempio: per il comportamento di sbirciatura quando i byte vengono passati a un io.Readerecc.

Controlla il suo codice sorgente per maggiori dettagli, qui .


5
Cosa intendi con "fuga"? Intendi dire escape nella stringa o semplicemente che i byte sottostanti possono essere esposti?
makhdumi,

1
@makhdumi Sì, 2 °, esposizione dei byte sottostanti.
Inanc Gumus

Vale la pena notare che strings.Builderimplementa i suoi metodi usando un ricevitore puntatore, che mi ha gettato per un momento. Di conseguenza, probabilmente ne creerei uno usando new.
Duncan Jones,

@DuncanJones Ho aggiunto una nota, tuttavia, poiché viene utilizzata principalmente per la memorizzazione nella cache dei dati, è normale utilizzare un puntatore ad esso durante la condivisione tra funzioni ecc. Nella stessa funzione, è possibile utilizzarla anche come non puntatore.
Inanc Gumus,

130

Esiste una funzione di libreria nel pacchetto stringhe chiamata Join: http://golang.org/pkg/strings/#Join

Uno sguardo al codice di Joinmostra un approccio simile alla funzione Append Kinopiko ha scritto: 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

21
Non funziona quando devi eseguire il loop su qualcosa che non è una stringa [].
Malcolm,

42

Ho appena confrontato la risposta più sopra pubblicata nel mio codice (una passeggiata ad albero ricorsiva) e il semplice operatore concat è in realtà più veloce di 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()
}

Questa operazione ha richiesto 0,81 secondi, mentre il seguente codice:

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
} 

ci sono voluti solo 0,61 secondi. Ciò è probabilmente dovuto al sovraccarico di creare il nuovo BufferString.

Aggiornamento: ho anche confrontato la joinfunzione e ha funzionato in 0,54 secondi.

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,"")
}

5
Credo che l'OP fosse più preoccupato della complessità della memoria piuttosto che della complessità del runtime, dato che ingenui concatenamenti di stringhe provocano ogni volta nuove allocazioni di memoria.
galaktor,

15
La bassa velocità potrebbe essere correlata all'uso di fmt.Fprint invece di buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will

Sono lieto di sapere che il mio metodo preferito di (strings.Join)corsa è il più veloce mentre da questo dire che (bytes.Buffer)è il vincitore!
Chetabahana,

23

È possibile creare una grande porzione di byte e copiarvi i byte delle stringhe brevi usando le sezioni di stringa. C'è una funzione fornita in "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;
}

Quindi, al termine delle operazioni, utilizzare string ( )la porzione grande di byte per convertirla nuovamente in una stringa.


È interessante che ci siano così tanti modi per farlo in Go.
Yitzhak,

11
In pratica, dice anche che l'idea è così utile che è stata catturata in un built-in. Quindi puoi sostituire la tua funzione con append(slice, byte...), a quanto pare.
Aktau,

23

Questa è la soluzione più veloce che non richiede di conoscere o calcolare prima la dimensione complessiva del buffer:

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

Secondo il mio benchmark , è più lento del 20% rispetto alla soluzione di copia (8,1 ns per append anziché 6,72 ns) ma ancora più veloce del 55% rispetto all'utilizzo dei byte.


23
package main

import (
  "fmt"
)

func main() {
    var str1 = "string1"
    var str2 = "string2"
    out := fmt.Sprintf("%s %s ",str1, str2)
    fmt.Println(out)
}

2
Benvenuto in Stack Overflow! Prenditi un momento per leggere la guida alla modifica nel Centro assistenza. La formattazione su Stack Overflow è diversa rispetto ad altri siti.
Rizier123

2
Sebbene questo frammento di codice possa risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che in futuro stai rispondendo alla domanda dei lettori e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice. Cerca anche di non aggiungere il tuo codice a commenti esplicativi, ciò riduce la leggibilità sia del codice che delle spiegazioni!
Rizier123

Soluzione semplice 👍
Finn,

22

Nota aggiunta nel 2018

Da Go 1.10 c'è un strings.Buildertipo, per favore dai un'occhiata a questa risposta per maggiori dettagli .

Risposta pre-201x

Il codice di riferimento di @ cd1 e altre risposte sono errati. b.Nnon dovrebbe essere impostato nella funzione benchmark. Viene impostato in modo dinamico dallo strumento go test per determinare se il tempo di esecuzione del test è stabile.

Una funzione di benchmark dovrebbe eseguire gli stessi b.Ntempi di test e il test all'interno del loop dovrebbe essere lo stesso per ogni iterazione. Quindi lo aggiusto aggiungendo un loop interno. Aggiungo anche benchmark per alcune altre soluzioni:

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)
    }
}

L'ambiente è OS X 10.11.6, Intel Core i7 a 2,2 GHz

Risultati del test:

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

Conclusione:

  1. CopyPreAllocateè il modo più veloce; AppendPreAllocateè abbastanza vicino a No.1, ma è più facile scrivere il codice.
  2. Concatha prestazioni pessime sia per la velocità che per l'utilizzo della memoria. Non usarlo.
  3. Buffer#Writee Buffer#WriteStringsono sostanzialmente gli stessi in termini di velocità, contrariamente a quanto ha detto @ Dani-Br nel commento. Considerare che stringè davvero []bytein Go, ha senso.
  4. bytes.Buffer utilizza fondamentalmente la stessa soluzione Copycon la tenuta di libri extra e altre cose.
  5. Copye Appendusa una dimensione bootstrap di 64, uguale a byte.Buffer
  6. Appendusa più memoria e alloc, penso che sia correlato all'algoritmo di crescita che usa. Non sta crescendo la memoria velocemente quanto i byte. Buffer

Suggerimento:

  1. Per un compito semplice come quello che vuole OP, vorrei usare Appendo AppendPreAllocate. È abbastanza veloce e facile da usare.
  2. Se è necessario leggere e scrivere il buffer contemporaneamente, utilizzare bytes.Bufferovviamente. È per questo che è progettato.

13

Il mio suggerimento originale era

s12 := fmt.Sprint(s1,s2)

Ma sopra la risposta usando bytes.Buffer - WriteString () è il modo più efficiente.

Il mio suggerimento iniziale utilizza la riflessione e un interruttore di tipo. Vedere (p *pp) doPrinte(p *pp) printArg
non esiste un'interfaccia universale Stringer () per i tipi di base, come avevo ingenuamente pensato.

Almeno, Sprint () utilizza internamente un byte. Buffer. così

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

è accettabile in termini di allocazioni di memoria.

=> La concatenazione Sprint () può essere utilizzata per un output di debug rapido.
=> Altrimenti usa bytes.Buffer ... WriteString


8
Non è integrato e non è efficiente.
peterSO,

L'importazione di un pacchetto (come fmt) significa che non è incorporato. È nella libreria standard.
Malcolm,

È lento solo perché usa la riflessione sui suoi argomenti. È efficace. Altrimenti non è meno efficiente
dell'unione

11

Espandendo la risposta di cd1: potresti usare append () invece di copy (). append () rende disposizioni anticipate sempre più grandi, costando un po 'più di memoria, ma risparmiando tempo. Ho aggiunto altri due benchmark nella parte superiore del tuo. Esegui localmente con

go test -bench=. -benchtime=100ms

Sul mio thinkpad T400s si ottiene:

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

4

Questa è la versione attuale del benchmark fornita da @ cd1 ( Go 1.8, linux x86_64) con le correzioni di bug menzionate da @icza e @PickBoy.

Bytes.Bufferè solo 7volte più veloce della concatenazione di stringhe diretta tramite +operatore.

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")
        }
    }
}

Tempi:

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op

Non penso che l'impostazione manuale di bN sia il modo giusto per utilizzare le funzioni di riferimento del pacchetto di test
PickBoy

@PickBoy, ti preghiamo di giustificare il tuo punto di vista. Perché pensi che b.Nsia una variabile pubblica?
Vitaly Isaev,

1
bN non dovrebbe essere impostato nella funzione benchmark. È impostato dinamicamente dallo strumento go test. Una funzione di benchmark dovrebbe eseguire gli stessi test bN volte, ma nel tuo codice (così come nel codice di @ cd1), ogni test nel loop è un test diverso (perché la lunghezza della stringa sta crescendo)
PickBoy

@PickBoy, se lasci che lo strumento di test go sia impostato in b.Nmodo dinamico, finirai con stringhe di lunghezza diversa in diversi casi di test. Vedi commento
Vitaly Isaev il

Ecco perché dovresti aggiungere un loop interno di un numero fisso di iterazioni, come 10000, all'interno del loop bN.
PickBoy

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()
}

1

Lo faccio usando il seguente: -

package main

import (
    "fmt"
    "strings"
)

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

Questo non affronta il problema di OP di costruire una stringa attraverso una serie di iterazioni, con un ciclo 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))
}

3
Si prega di non inviare risposte solo in codice. Fornisci una spiegazione su cosa fa questo codice e perché è la soluzione.
Korashen,

-1

risultato del benchmark con statistiche di allocazione della memoria. controlla il codice di riferimento su github .

usa stringhe.Builder per ottimizzare le prestazioni.

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

per favore dai credito a @ cd1 per i casi di test originali che stai costruendo qui.
colm.anseo,

-2
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))

5
Questa soluzione è molto lenta, poiché utilizza la riflessione, analizza la stringa di formato e crea una copia dei dati per la []byte(s1)conversione. Confrontandolo con altre soluzioni pubblicate, puoi nominare un singolo vantaggio della tua soluzione?
punti

-5

strings.Join() dal pacchetto "stringhe"

Se hai un tipo non corrispondente (come se stai provando a unire un int e una stringa), fai RANDOMTYPE (cosa che vuoi cambiare)

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, ""))
}

Produzione :

hello all you people in here

4
Questo codice non viene nemmeno compilato: strings.Join()accetta solo 2 parametri: una sezione e un separatore string.
Icza,

questo non può aiutare
Anshu,

aggiungi alcune modifiche qui.
Anshu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.