Hareket halindeyken dizeleri verimli bir şekilde nasıl birleştirebilirim?


727

Go'da a string, ilkel bir türdür, yani salt okunurdur ve her manipülasyonu yeni bir dize oluşturur.

Sonuçta elde edilen dizenin uzunluğunu bilmeden dizeleri birçok kez birleştirmek istiyorsam, bunu yapmanın en iyi yolu nedir?

Saf yol şöyle olurdu:

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

ama bu çok verimli görünmüyor.



1
Not: Bu soru ve çoğu cevap daha önce yazılmış gibi görünüyor append(), bu da bunun için iyi bir çözüm. Gibi hızlı performans copy()gösterecek, ancak kapasite yeterli değilse yeni bir destek dizisi tahsis etmek anlamına gelse bile ilk önce dilim büyüyecektir. bytes.Bufferek kolaylık yöntemlerini istiyorsanız veya kullandığınız paket beklediğinde yine de anlamlı.
thomasrutter

7
Sadece "çok verimsiz gözükmüyor"; belirli bir sorunu var, şimdiye kadar kazandığımız her yeni CS dışı kiralama işe ilk birkaç hafta içinde giriyor. İkinci dereceden - O (n * n). Numara, sekansın düşünün: 1 + 2 + 3 + 4 + .... Bu n*(n+1)/2, bir taban üçgeninin alanı n. Bir döngüye değiştirilemeyen dizeler eklediğinizde, 1 boyutunu, sonra 2 boyutunu, sonra 3 boyutunu vb. Ayırırsınız. Bu ikinci dereceden kaynak tüketimi kendini bundan daha fazla gösterir.
Rob

Yanıtlar:


856

Yeni yol:

Go 1.10'dan bir strings.Buildertür var, lütfen daha fazla ayrıntı için bu cevaba bir göz atın .

Eski yol:

bytesPaketi kullanın . Uygulayan bir Buffertürü vardır 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())
}

Bunu O (n) zamanda yapar.


24
println yerine (string (buffer.Bytes ())); kullanımı sadece println (buffer.String ()) yapabilirdi
FigmentEngine

26
Bunun yerine buffer := bytes.NewBufferString(""), yapabilirsiniz var buffer bytes.Buffer. Ayrıca noktalı virgüllere de ihtiyacınız yok :).
crazy2be

66
İnanılmaz derecede hızlı. Benim programda bazı naif "+" dize concat 3 dakika 1,3 saniye gidin .
Malcolm

10
"O (n) süresi" için +1; Bunun gibi daha fazla açıklama yapmanın önemli olduğunu düşünüyorum.
contradictioned

8
Git 1.10 ekler strings.Builder nihai hedefiniz bir dize olduğunda hızlı bytes.Buffer gibidir ama.
Josh Bleecher Snyder

272

Dizeleri birleştirmenin en etkili yolu yerleşik işlevi kullanmaktır copy. Testlerimde bu yaklaşım, kullanımdan ~ 3 kat daha bytes.Bufferhızlı ve operatörü kullanmaktan çok daha hızlı (~ 12.000x) +. Ayrıca, daha az bellek kullanır.

Bunu kanıtlamak için bir test davası oluşturdum ve işte sonuçlar:

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

Test için kod aşağıdadır:

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
Bayt.Buffer temel olarak kopya ile aynı yapmalıdır (sanırım bazı ekstra defter tutma ile) ve hız o kadar da farklı değil. Bu yüzden onu kullanırım :). Fark tamponun 0 bayt ile başlamasıdır, bu yüzden yeniden tahsis etmesi gerekir (bu sanırım biraz daha yavaş görünüyor). Yine de kullanımı daha kolay.
Aktau

5
buffer.Write(bayt)% 30 daha hızlıdır buffer.WriteString. [Verileri şu şekilde edinebiliyorsanız yararlıdır []byte]
Dani-Br

34
Kıyaslama sonuçlarının bozuk olduğunu ve orijinal olmadığını unutmayın. Farklı karşılaştırma işlevleri farklı değerleriyle çağrılır b.Nve bu nedenle gerçekleştirilecek aynı görevin yürütme süresini karşılaştırmazsınız (örneğin bir işlev 1,000dizeler ekleyebilir , diğeri 10,000ortalamada büyük bir fark yaratabilir BenchmarkConcat()Örneğin , 1 ek süre ). Her durumda aynı ekleme sayısını kullanmalısınız (kesinlikle değil b.N) ve foraralığın gövdesindeki tüm birleştirmeyi yapmalısınız b.N(yani, 2 fordöngü gömülü).
icza

18
Ayrıca, kopya karşılaştırması, diğer ölçütlere dahil edilen ayırma süresinin açıkça göz ardı edilmesiyle çarpıktır.
gha.st

6
Ek olarak, kopya kıyaslama sonucu ortaya çıkan dizenin uzunluğunu bilmeye dayanır.
Skarllot

227

Go 1.10+ sürümünde strings.Builder, burada .

Bir Builder, Write yöntemlerini kullanarak verimli bir şekilde bir dize oluşturmak için kullanılır. Bellek kopyalamayı en aza indirir. Sıfır değeri kullanıma hazırdır.


Misal

Neredeyse aynı 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())
}

Bunu oyun alanında görmek için tıklayın .


Not

  • Temel verileri önbelleğe aldığından bir StringBuilder değerini kopyalamayın.
  • Bir StringBuilder değerini paylaşmak istiyorsanız, buna bir işaretçi kullanın.

Desteklenen Arayüzler

StringBuilder yöntemleri mevcut arayüzler göz önünde bulundurularak uygulanmaktadır. Böylece kodunuzda kolayca yeni Oluşturucu türüne geçebilirsiniz.


Bayttan farklar.

  • Sadece büyüyebilir veya sıfırlanabilir.

  • Yanlışlıkla kopyalanmasını önleyen yerleşik bir copyCheck mekanizması vardır:

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

  • Gelen bytes.Bufferbir böyle yatan bayt erişebilir: (*Buffer).Bytes().

    • strings.Builder bu sorunu önler.
    • Bazen, bunun yerine bir sorun değildir ve arzu edilir.
    • Örneğin: Baytlar io.Readervs.'ye geçtiğinde gözetleme davranışı için .

Daha fazla ayrıntı için kaynak koduna buradan göz atın .


5
'Kaçış' ile ne demek istiyorsun? Dizedeki kaçışlar mı yoksa sadece alttaki baytların açığa çıkabileceği mi demek istediniz?
makhdumi

1
@makhdumi Evet, 2., altta yatan baytlara maruz kalma.
İnanç Gümüş

Dikkat çekmeye değer strings.Builder, bir an için beni fırlatan bir işaretçi alıcısı kullanarak yöntemlerini uygular. Sonuç olarak, muhtemelen kullanarak bir tane oluşturmak istiyorum new.
Duncan Jones

@DuncanJones Bir not ekledim, ancak çoğunlukla önbellek verileri için kullanıldığından, işlevler vb. Arasında paylaşırken bir işaretçi kullanmak normaldir. Aynı fonkta, işaretçi olmayan olarak da kullanabilirsiniz.
İnanç Gümüş

130

Denilen dizeleri paketinde bir kütüphane işlevi yoktur Join: http://golang.org/pkg/strings/#Join

JoinKinopiko'nun Ekleme fonksiyonuna benzer bir yaklaşımı gösteren kodlara bir bakış: https://golang.org/src/strings/strings.go#L420

Kullanımı:

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
Bir [] dizesi olmayan bir şey üzerinde döngü yapmanız gerektiğinde çalışmaz.
Malcolm

42

Ben sadece kendi kodu (özyinelemeli bir ağaç yürüyüş) yukarıda yayınlanan üst cevap karşılaştırdı ve basit concat operatörü aslında daha hızlı 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()
}

Bu 0,81 saniye sürerken, aşağıdaki kod:

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
} 

sadece 0.61 saniye sürdü. Bu muhtemelen yenisini yaratma yükünden kaynaklanmaktadır BufferString.

Güncelleme:join İşlevi de karşılaştırdım ve 0,54 saniye içinde çalıştı.

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
Saf dize birleştirme her zaman yeni bellek ayırmaları ile sonuçlanan gerçeği göz önüne alındığında, OP, çalışma zamanı karmaşıklığı yerine bellek karmaşıklığı hakkında daha fazla endişeli olduğuna inanıyorum.
galaktor

15
Bunun yavaş hızı fmt kullanmakla ilgili olabilir. Yerine Parmak İzi buffer.WriteString("\t"); buffer.WriteString(subs[i]);
Robert Jack Will

Bildiğim sevindim o benim tercih edilen yöntem (strings.Join)en hızlı şekilde vadede ise gelen bu söyleyerek (bytes.Buffer)kazanır!
Mart'ta Chetabahana

23

Büyük bir dilim bayt oluşturabilir ve kısa dizelerin baytlarını dize dilimleri kullanarak kopyalayabilirsiniz. "Effective Go" da verilen bir fonksiyon vardır:

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

Daha sonra işlemler bittiğinde, string ( )tekrar bir dizeye dönüştürmek için büyük bayt diliminde kullanın .


Go'da bunu yapmanın birçok yolu olması ilginç.
Yitzhak

11
Etkili bir şekilde, bu fikrin o kadar yararlı olduğunu söyler, bir yapıda yakalanır. Yani fonksiyonunuzu yerine değiştirebilirsiniz append(slice, byte...), öyle görünüyor.
Aktau

23

Bu, önce genel tampon boyutunu bilmenizi veya hesaplamanızı gerektirmeyen en hızlı çözümdür:

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

Benim By kriter , hala% 55 daha hızlı bytes.Buffer kullanmaktan daha (yerine 6.72ns daha append başına 8.1ns) ancak% 20 daha yavaş kopyalama çözümü daha var.


23
package main

import (
  "fmt"
)

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

2
Stack Overflow'a hoş geldiniz! Yardım merkezindeki düzenleme yardımını okumak için bir dakikanızı ayırın . Yığın Taşması üzerinde biçimlendirme diğer sitelerden farklıdır.
Rizier123

2
Bu kod snippet'i soruyu çözebilir, ancak bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olur. Gelecekte okuyucular için soruyu cevapladığınızı ve bu kişilerin kod önerinizin nedenlerini bilmeyebileceğini unutmayın. Lütfen kodunuzu açıklayıcı yorumlarla doldurmamaya çalışın, bu hem kodun hem de açıklamaların okunabilirliğini azaltır!
Rizier123

Basit çözüm 👍
Finn

22

2018'de not eklendi

Go 1.10'dan bir strings.Buildertür var, lütfen daha fazla ayrıntı için bu cevaba bir göz atın .

201x öncesi cevap

@ Cd1 ve diğer yanıtların karşılaştırma kodu yanlış. b.Nkarşılaştırmalı değerlendirme işlevinde ayarlanmamış olması gerekir. Testin yürütme süresinin kararlı olup olmadığını belirlemek için go test aracı tarafından dinamik olarak ayarlanır.

Bir kıyaslama işlevi aynı test b.Nsürelerini çalıştırmalı ve döngü içindeki test her yineleme için aynı olmalıdır. Bu yüzden bir iç döngü ekleyerek düzeltirim. Ayrıca diğer bazı çözümler için kıyaslamalar da ekliyorum:

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

Ortam OS X 10.11.6, 2.2 GHz Intel Core i7'dir

Test sonuçları:

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

Sonuç:

  1. CopyPreAllocateen hızlı yoldur; AppendPreAllocateNo.1'e oldukça yakın, ancak kodu yazmak daha kolay.
  2. Concathem hız hem de bellek kullanımı için gerçekten kötü bir performansa sahip. Kullanma.
  3. Buffer#Writeve Buffer#WriteString@ Dani-Br'nin yorumda söylediklerinin aksine, temel olarak hızda aynı. Düşünüldüğünde stringgerçekten de []bytemantıklı, Go.
  4. Tampon temel olarak Copyekstra kitap tutma ve diğer şeylerle aynı çözümü kullanır .
  5. Copyve Appendbaytlarla aynı 64 olan bir bootstrap boyutu kullanın.
  6. Appenddaha fazla bellek ve alaşım kullanın, bence kullandığımız büyüme algoritmasıyla ilgili. Belleği bayt kadar hızlı büyütmüyor.

Öneri:

  1. OP'nin istediği gibi basit görevler için, Appendya da kullanacağım AppendPreAllocate. Yeterince hızlı ve kullanımı kolaydır.
  2. Aynı anda tamponu okumak ve yazmak gerekirse bytes.Buffer, elbette kullanın . Bunun için tasarlandı.

13

Orijinal önerim

s12 := fmt.Sprint(s1,s2)

Ancak bytes.Buffer - WriteString () kullanarak yukarıdaki cevap en etkili yoldur.

İlk önerim yansıma ve yazım anahtarı kullanıyor. Bkz (p *pp) doPrintve(p *pp) printArg
ben saf saf tahmin ettiği gibi evrensel Stringer () arayüzü, temel türleri için mevcuttur.

En azından Sprint () dahili olarak bir bytes.Buffer kullanır. Böylece

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

bellek ayırma açısından kabul edilebilir.

=> Hızlı hata ayıklama çıktısı için Sprint () birleşimi kullanılabilir.
=> Aksi takdirde bayt kullanır.Buffer ... WriteString


8
Yerleşik değildir ve verimli değildir.
peterSO

Bir paketi (fmt gibi) içe aktarmak paketin yerleşik olmadığı anlamına gelir. Standart kütüphanede.
Malcolm

Sadece argümanları üzerinde düşünmeyi kullandığından yavaştır. Etkili. Aksi takdirde,
dizelerle

11

CD1'in cevabına genişleme: copy () yerine append () yöntemini kullanabilirsiniz. append () her zamankinden daha fazla ön provizyon yapar, biraz daha fazla hafızaya mal olur, ancak zaman kazandırır. Üstünüze iki karşılaştırma ölçütü daha ekledim . İle yerel olarak çalıştır

go test -bench=. -benchtime=100ms

Thinkpad T400'lerimde aşağıdakiler elde edilir:

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

4

@ İc1 ve @PickBoy tarafından belirtilen hata düzeltmeleri ile @ cd1 ( Go 1.8, linux x86_64) tarafından sağlanan gerçek ölçüt sürümüdür.

Bytes.Buffer, operatör 7aracılığıyla doğrudan dize birleştirmeden sadece kat daha hızlıdır +.

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

Zamanlamaları:

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

El bN test paketinin kullanımı kriter fonksiyonlarına doğru yolu ayarı düşünmüyor musunuz
PickBoy

@PickBoy, lütfen bakış açınızı gerekçelendirin. Neden b.Nkamu değişkeni olduğunu düşünüyorsunuz ?
Vitaly Isaev

1
bN'nin karşılaştırma işlevine ayarlanması beklenmemektedir. Go test aracı tarafından dinamik olarak ayarlanır. Bir kıyaslama işlevi aynı testi bN kez çalıştırmalıdır, ancak kodunuzda (@ cd1 kodunun yanı sıra), döngüdeki her test farklı bir testtir (dizenin uzunluğu büyüdüğü için)
PickBoy

@PickBoy, go test aracının b.Ndinamik olarak ayarlanmasına izin verirseniz, farklı test senaryolarında farklı uzunlukta bir dizeyle dolanırsınız . Bkz yorumunu
Vitaly İsaev

Bu yüzden bN döngüsünün içine 10000 gibi sabit sayıda yinelemeden oluşan bir iç döngü eklemelisiniz.
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

Aşağıdakileri kullanarak yaparım: -

package main

import (
    "fmt"
    "strings"
)

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

Bu, OP'nin for döngüsü ile bir dizi yineleme yoluyla dize oluşturma konusunu ele almaz.
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
Lütfen sadece kodları göndermeyin. Lütfen bu kodun ne yaptığını ve neden çözüm olduğunu açıklayınız.
Korashen

-1

bellek ayırma istatistikleriyle karşılaştırmalı sonuç. github'daki karşılaştırma kodunu kontrol edin .

Performansı optimize etmek için dizeleri kullanın.

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

lütfen burada oluşturduğunuz orijinal test senaryoları için @ cd1'e kredi verin.
colm.anseo

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

5
Bu çok yavaş bir çözümdür, çünkü yansımayı kullanır, format dizesini ayrıştırır ve []byte(s1)dönüşüm için verilerin bir kopyasını oluşturur . Yayınlanan diğer çözümlerle karşılaştırıldığında, çözümünüzün tek bir avantajını söyleyebilir misiniz?
pts

-5

strings.Join() "dizeler" paketinden

Bir tür uyuşmazlığınız varsa (int ve dizeye katılmaya çalışıyorsanız gibi), RANDOMTYPE (değiştirmek istediğiniz şey)

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

Çıktı :

hello all you people in here

4
Bu kod bile derlenmez: strings.Join()sadece 2 parametre alır: bir dilim ve bir ayırıcı string.
icza

bu yardımcı olamaz
Anshu

buraya bazı değişiklikler ekleyin.
Anshu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.