Как эффективно объединить строки в Go? - PullRequest
637 голосов
/ 19 ноября 2009

В Go, string является примитивным типом, что означает, что он доступен только для чтения, и каждое его манипулирование создаст новую строку.

Так что, если я хочу многократно объединять строки, не зная длины получаемой строки, каков наилучший способ сделать это?

Наивный путь будет:

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

но это не кажется очень эффективным.

Ответы [ 20 ]

10 голосов
/ 25 января 2015

Расширение ответа CD1: Вы можете использовать append () вместо copy (). Функция append () обеспечивает более продвинутые предварительные условия, стоит немного больше памяти, но экономит время. Я добавил еще два теста в верхней части вашего. Запуск локально с

go test -bench=. -benchtime=100ms

На моем ThinkPad T400s это выдает:

BenchmarkAppendEmpty    50000000         5.0 ns/op
BenchmarkAppendPrealloc 50000000         3.5 ns/op
BenchmarkCopy           20000000        10.2 ns/op
2 голосов
/ 29 марта 2017

Это актуальная версия бенчмарка, предоставляемая @ cd1 (Go 1.8, linux x86_64) с исправлениями ошибок, упомянутых @icza и @ PickBoy.

Bytes.Buffer только в 7 раз быстрее, чем прямая конкатенация строк с помощью оператора +.

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

Тайминги:

BenchmarkConcat-4                             300000          6869 ns/op
BenchmarkBuffer-4                            1000000          1186 ns/op
1 голос
/ 18 сентября 2018

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 голос
/ 26 января 2018

Я делаю это используя следующее: -

package main

import (
    "fmt"
    "strings"
)

func main (){
    concatenation:= strings.Join([]string{"a","b","c"},"") //where second parameter is a separator. 
    fmt.Println(concatenation) //abc
}
0 голосов
/ 11 мая 2016

strings.Join() из пакета "Струны"

Если у вас несоответствие типов (например, если вы пытаетесь объединить int и строку), вы делаете RANDOMTYPE (вещь, которую вы хотите изменить)

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

Выход:

hello all you people in here
0 голосов
/ 05 сентября 2018

результат теста со статистикой выделения памяти. проверьте код теста на github .

использование строк. Билдер для оптимизации производительности.

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
0 голосов
/ 09 августа 2018
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))
}
0 голосов
/ 04 сентября 2013
s := fmt.Sprintf("%s%s", []byte(s1), []byte(s2))
0 голосов
/ 23 ноября 2017

Для тех, кто приехал из мира Java, где у нас есть StringBuilder для эффективной конкатенации строк, похоже, что последняя версия go имеет свой эквивалент и называется Builder: https://github.com/golang/go/blob/master/src/strings/builder.go

0 голосов
/ 22 июня 2017

Посмотрите на библиотеку golang strconv , предоставляющую доступ к нескольким функциям AppendXX, что позволяет нам объединять строки со строками и другими типами данных.

...