Cara menggabungkan string secara efisien

727

Di Go, a stringadalah tipe primitif, yang berarti hanya baca, dan setiap manipulasi akan membuat string baru.

Jadi jika saya ingin menyatukan string berkali-kali tanpa mengetahui panjang dari string yang dihasilkan, apa cara terbaik untuk melakukannya?

Cara naifnya adalah:

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

tetapi itu tampaknya tidak terlalu efisien.

Randy Sugianto 'Yuku'
sumber
7
Satu bangku
Ivan Black
1
Catatan: Pertanyaan ini dan sebagian besar jawaban tampaknya telah ditulis sebelum append()masuk ke dalam bahasa, yang merupakan solusi yang bagus untuk ini. Ini akan melakukan seperti cepat copy()tetapi akan menumbuhkan irisan pertama bahkan jika itu berarti mengalokasikan array dukungan baru jika kapasitasnya tidak cukup. bytes.Buffermasih masuk akal jika Anda menginginkan metode kenyamanan tambahan atau jika paket yang Anda gunakan mengharapkannya.
thomasrutter
7
Itu tidak hanya "tampak sangat tidak efisien"; itu memiliki masalah khusus bahwa setiap karyawan non-CS baru yang pernah kami jumpai dalam beberapa minggu pertama di tempat kerja. Ini kuadrat - O (n * n). Pikirkan urutan nomor: 1 + 2 + 3 + 4 + .... Itu n*(n+1)/2, area segitiga alas n. Anda mengalokasikan ukuran 1, lalu ukuran 2, lalu ukuran 3, dll ketika Anda menambahkan string yang tidak dapat diubah dalam satu lingkaran. Konsumsi sumber daya kuadratik ini memanifestasikan dirinya dalam lebih dari sekadar ini.
Rob

Jawaban:

856

Jalan baru:

Dari Go 1.10 ada strings.Buildertipe, silakan lihat jawaban ini untuk lebih detail .

Cara lama:

Gunakan bytespaket. Ini memiliki Buffertipe yang mengimplementasikan 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())
}

Ini melakukannya dalam waktu O (n).

pemasar
sumber
24
bukannya println (string (buffer.Bytes ())); gunakan hanya bisa melakukan println (buffer.String ())
FigmentEngine
26
Alih-alih buffer := bytes.NewBufferString(""), Anda bisa melakukannya var buffer bytes.Buffer. Anda juga tidak memerlukan titik koma itu :).
crazy2be
66
Sangat cepat. Membuat beberapa string "+" naif dalam program saya berjalan dari 3 menit menjadi 1,3 detik .
Malcolm
10
+1 untuk "O (n) waktu"; Saya pikir penting untuk membuat lebih banyak komentar seperti ini.
contradictioned
8
Go 1.10 menambahkan strings.Builder , yang seperti byte. Buffer tetapi lebih cepat ketika tujuan akhir Anda adalah string.
Josh Bleecher Snyder
272

Cara paling efisien untuk menggabungkan string adalah menggunakan fungsi builtin copy. Dalam pengujian saya, pendekatan itu ~ 3x lebih cepat daripada menggunakan bytes.Bufferdan jauh lebih cepat (~ 12.000x) daripada menggunakan operator +. Selain itu, ia menggunakan lebih sedikit memori.

Saya telah membuat test case untuk membuktikan ini dan berikut hasilnya:

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 bawah ini adalah kode untuk pengujian:

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
sumber
6
Bytes.Buffer pada dasarnya harus melakukan hal yang sama dengan salinan (saya kira beberapa pembukuan tambahan) dan kecepatannya tidak berbeda. Jadi saya akan menggunakan itu :). Perbedaannya adalah bahwa buffer dimulai dengan 0 byte sehingga harus merealokasi (ini membuatnya tampak sedikit lebih lambat kurasa). Lebih mudah digunakan.
Aktau
5
buffer.Write(byte) lebih cepat 30% dari buffer.WriteString. [berguna jika Anda bisa mendapatkan data sebagai []byte]
Dani-Br
34
Perhatikan bahwa hasil patokan terdistorsi dan tidak asli. Fungsi benchmark yang berbeda akan dipanggil dengan nilai yang berbeda b.N, dan jadi Anda tidak membandingkan waktu eksekusi dari tugas yang sama untuk dijalankan (mis. Satu fungsi mungkin menambahkan 1,000string, yang lain mungkin menambahkan 10,000yang dapat membuat perbedaan besar dalam rata-rata waktu 1 tambahkan, BenchmarkConcat()misalnya). Anda harus menggunakan jumlah append yang sama dalam setiap kasus (tentu saja tidak b.N), dan melakukan semua penggabungan di dalam tubuh formulai b.N(yaitu, 2 forloop tertanam).
icza
18
Selain itu, tolok ukur salinan cenderung miring dengan mengabaikan waktu yang dibutuhkan alokasi, yang termasuk dalam tolok ukur lainnya.
gha.st
6
Selain itu, tolok ukur salinan bergantung pada mengetahui panjang string yang dihasilkan.
Skarllot
227

Di Go 1.10+ ada strings.Builder, di sini .

Builder digunakan untuk membangun string secara efisien menggunakan metode Write. Ini meminimalkan penyalinan memori. Nilai nol siap digunakan.


Contoh

Hampir sama dengan 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())
}

Klik untuk melihat ini di taman bermain .


Catatan

  • Jangan menyalin nilai StringBuilder karena cache data yang mendasarinya.
  • Jika Anda ingin membagikan nilai StringBuilder, gunakan pointer ke sana.

Antarmuka yang didukung

Metode StringBuilder diimplementasikan dengan mempertimbangkan antarmuka yang ada. Sehingga Anda dapat beralih ke tipe Builder baru dengan mudah di kode Anda.


Perbedaan dari bytes.Buffer

  • Itu hanya bisa tumbuh atau diatur ulang.

  • Ini memiliki mekanisme copyCheck bawaan yang mencegah penyalinannya secara tidak sengaja:

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

  • Dalam bytes.Buffer, satu dapat mengakses byte yang mendasari seperti ini: (*Buffer).Bytes().

    • strings.Builder mencegah masalah ini.
    • Kadang-kadang, ini bukan masalah dan malah yang diinginkan.
    • Sebagai contoh: Untuk perilaku mengintip ketika byte diteruskan ke io.Readerdll.

Lihat kode sumbernya untuk lebih jelasnya, di sini .

Inanc Gumus
sumber
5
Apa yang Anda maksud dengan 'melarikan diri'? Apakah maksud Anda lolos dalam string, atau hanya bahwa byte yang mendasarinya dapat diekspos?
makhdumi
1
@ makhdumi Ya, 2, paparan byte yang mendasarinya.
Inanc Gumus
Perlu dicatat strings.Buildermengimplementasikan metodenya menggunakan penerima pointer, yang membuat saya sejenak. Akibatnya, saya mungkin akan membuatnya menggunakan new.
Duncan Jones
@DuncanJones Saya telah menambahkan catatan, karena sebagian besar digunakan untuk caching data, itu normal untuk menggunakan pointer ketika membagikannya di func dll. Dalam func yang sama, Anda dapat menggunakannya sebagai non-pointer juga.
Inanc Gumus
130

Ada fungsi perpustakaan dalam paket string yang disebut Join: http://golang.org/pkg/strings/#Join

Melihat kode Joinmenunjukkan pendekatan yang mirip dengan fungsi Tambah Kinopiko menulis: https://golang.org/src/strings/strings.go#L420

Pemakaian:

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
sumber
21
Tidak berfungsi saat Anda harus mengulang sesuatu yang bukan string [].
Malcolm
42

Saya baru saja membandingkan jawaban teratas yang diposting di atas dalam kode saya sendiri (pohon rekursif berjalan) dan operator concat sederhana sebenarnya lebih cepat daripada 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()
}

Ini memakan waktu 0,81 detik, sedangkan kode berikut:

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
} 

hanya butuh 0,61 detik. Ini mungkin karena overhead menciptakan yang baru BufferString.

Pembaruan: Saya juga membandingkan joinfungsi dan menjalankannya dalam 0,54 detik.

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
sumber
5
Saya percaya OP lebih peduli tentang kompleksitas memori daripada kompleksitas runtime, mengingat fakta bahwa rangkaian string naif menghasilkan alokasi memori baru setiap kali.
galaktor
15
Kecepatan lambat ini mungkin terkait dengan menggunakan fmt. Cetak bukan buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will
Saya senang mengetahui bahwa metode (strings.Join)lari pilihan saya sebagai yang tercepat sementara dari pepatah ini(bytes.Buffer) adalah pemenangnya!
Chetabahana
23

Anda bisa membuat irisan besar byte dan menyalin byte string pendek ke dalamnya menggunakan irisan string. Ada fungsi yang diberikan dalam "Efektif 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;
}

Kemudian ketika operasi selesai, gunakan string ( )potongan besar byte untuk mengubahnya menjadi string lagi.


sumber
Sangat menarik bahwa ada begitu banyak cara untuk melakukan ini di Go.
Yitzhak
11
Secara efektif, ia juga mengatakan bahwa ide itu sangat berguna sehingga ditangkap dalam builtin. Jadi Anda dapat mengganti fungsi Anda dengan append(slice, byte...), sepertinya.
Aktau
23

Ini adalah solusi tercepat yang tidak mengharuskan Anda untuk mengetahui atau menghitung ukuran buffer keseluruhan terlebih dahulu:

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

Menurut tolok ukur saya , ini 20% lebih lambat dari solusi salin (8.1ns per append daripada 6.72ns) tetapi masih 55% lebih cepat daripada menggunakan bytes.Buffer.

rog
sumber
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
sumber
2
Selamat Datang di Stack Overflow! Luangkan waktu sejenak untuk membaca bantuan pengeditan di pusat bantuan. Memformat pada Stack Overflow berbeda dari situs lain.
Rizier123
2
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda. Tolong juga cobalah untuk tidak membuat kerumunan kode Anda dengan komentar penjelasan, ini mengurangi keterbacaan kode dan penjelasan!
Rizier123
Solusi sederhana 👍
Finn
22

Catatan ditambahkan pada 2018

Dari Go 1.10 ada strings.Buildertipe, silakan lihat jawaban ini untuk lebih detail .

Pra-201x menjawab

Kode tolok ukur @ cd1 dan jawaban lain salah. b.Ntidak seharusnya diatur dalam fungsi benchmark. Ini ditetapkan oleh alat uji go secara dinamis untuk menentukan apakah waktu pelaksanaan tes stabil.

Fungsi benchmark harus menjalankan waktu tes yang sama b.Ndan tes di dalam loop harus sama untuk setiap iterasi. Jadi saya memperbaikinya dengan menambahkan loop dalam. Saya juga menambahkan tolok ukur untuk beberapa solusi lain:

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

Lingkungan adalah OS X 10.11.6, 2.2 GHz Intel Core i7

Hasil tes:

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

Kesimpulan:

  1. CopyPreAllocateadalah cara tercepat; AppendPreAllocatecukup dekat dengan No.1, tetapi lebih mudah untuk menulis kode.
  2. Concatmemiliki kinerja yang sangat buruk baik untuk kecepatan dan penggunaan memori. Jangan gunakan itu.
  3. Buffer#Writedan Buffer#WriteStringpada dasarnya sama dalam kecepatan, bertentangan dengan apa yang dikatakan @ Dani-Br dalam komentar. Mengingat stringmemang[]byte dalam Go, itu masuk akal.
  4. bytes.Buffer pada dasarnya menggunakan solusi yang sama seperti Copydengan pembukuan tambahan dan hal-hal lainnya.
  5. Copydan Appendgunakan ukuran bootstrap 64, sama seperti bytes.Buffer
  6. Appendmenggunakan lebih banyak memori dan allocs, saya pikir itu terkait dengan algoritma tumbuh yang digunakannya. Ini tidak menumbuhkan memori secepat byte. Buffer

Saran:

  1. Untuk tugas sederhana seperti apa yang diinginkan OP, saya akan menggunakan Appendatau AppendPreAllocate. Cukup cepat dan mudah digunakan.
  2. Jika perlu membaca dan menulis buffer pada saat bersamaan, gunakan bytes.Buffersaja. Untuk itulah dirancang.
PickBoy
sumber
13

Saran asli saya adalah

s12 := fmt.Sprint(s1,s2)

Tetapi jawaban di atas menggunakan bytes.Buffer - WriteString () adalah cara yang paling efisien.

Saran awal saya menggunakan refleksi dan saklar jenis. Lihat (p *pp) doPrintdan(p *pp) printArg
Tidak ada antarmuka Stringer () universal untuk tipe dasar, seperti yang saya pikir naif.

Paling tidak, Sprint () secara internal menggunakan bytes.Buffer. Jadi

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

dapat diterima dalam hal alokasi memori.

=> Sprint () concatenation dapat digunakan untuk hasil debug cepat.
=> Kalau tidak gunakan bytes.Buffer ... WriteString

Peter Buchmann
sumber
8
Itu tidak built-in dan tidak efisien.
peterSO
Mengimpor paket (seperti fmt) berarti itu bukan bawaan. Itu ada di perpustakaan standar.
Malcolm
Ini lambat hanya karena menggunakan refleksi atas argumennya. Itu efisien. Kalau tidak, itu tidak kalah efisien daripada bergabung dengan string.
Bergabunglah
11

Memperluas jawaban cd1: Anda mungkin menggunakan append () alih-alih copy (). append () membuat ketentuan sebelumnya semakin besar, menghabiskan sedikit lebih banyak memori, tetapi menghemat waktu. Saya menambahkan dua tolok ukur di bagian atas Anda. Jalankan secara lokal dengan

go test -bench=. -benchtime=100ms

Di T400s thinkpad saya menghasilkan:

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

Ini adalah versi aktual dari benchmark yang disediakan oleh @ cd1 ( Go 1.8, linux x86_64) dengan perbaikan bug yang disebutkan oleh @icza dan @PickBoy.

Bytes.Bufferhanya 7kali lebih cepat daripada penggabungan string langsung melalui +operator.

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

Pengaturan waktu:

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op
Vitaly Isaev
sumber
Saya tidak berpikir mengatur bN secara manual adalah cara yang tepat untuk menggunakan fungsi benchmark dari paket pengujian
PickBoy
@ PickBoy, mohon benarkan sudut pandang Anda. Menurut Anda mengapa b.Nvariabel publik?
Vitaly Isaev
1
bN tidak seharusnya diatur dalam fungsi benchmark. Ini ditetapkan oleh alat uji go secara dinamis. Fungsi benchmark harus menjalankan tes yang sama dengan kali, tetapi dalam kode Anda (dan juga kode @ cd1), setiap tes dalam loop adalah tes yang berbeda (karena panjang string tumbuh)
PickBoy
@ PickBoy, jika Anda membiarkan alat tes go diatur b.Nsecara dinamis, Anda akan berakhir dengan string dengan panjang yang berbeda di berbagai kasus uji. Lihat komentar
Vitaly Isaev
Itu sebabnya Anda harus menambahkan loop dalam jumlah iterasi yang tetap, seperti 10.000, di dalam loop bN.
PickBoy
3

goutils. Bergabunglah

 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
sumber
1

Saya melakukannya menggunakan yang berikut: -

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
sumber
Ini tidak membahas masalah OP membangun string melalui serangkaian iterasi, dengan for loop.
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
sumber
3
Tolong jangan posting jawaban hanya kode. Tolong beri penjelasan apa yang kode ini lakukan dan mengapa ini solusinya.
Korashen
-1

hasil benchmark dengan statistik alokasi memori. periksa kode tolok ukur di github .

gunakan strings.Builder untuk mengoptimalkan kinerja.

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
sumber
tolong beri kredit kepada @ cd1 untuk kasus uji asli yang Anda buat di sini.
colm.anseo
-2
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))
pengguna2288856
sumber
5
Solusi ini sangat lambat, karena menggunakan refleksi, mem-parsing string format, dan membuat salinan data untuk []byte(s1)konversi. Membandingkannya dengan solusi lain yang diposting, dapatkah Anda menyebutkan satu keuntungan dari solusi Anda?
Poin
-5

strings.Join() dari paket "string"

Jika Anda memiliki tipe ketidakcocokan (seperti jika Anda mencoba untuk bergabung dengan int dan string), Anda melakukan RANDOMTYPE (hal yang ingin Anda ubah)

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

Keluaran:

hello all you people in here
Liam
sumber
4
Kode ini bahkan tidak dapat dikompilasi: strings.Join()hanya membutuhkan 2 parameter: slice dan separator string.
icza
ini tidak bisa membantu
Anshu
tambahkan beberapa perubahan di sini.
Anshu