Какой способ построения (конкатенации) строк в цикле является наиболее эффективным в Go и почему?

Ответ

Наиболее эффективным способом является использование strings.Builder. Это связано с тем, как Go управляет памятью для строк.

Почему strings.Builder — лучший выбор:

Строки в Go неизменяемы (immutable). Любая операция конкатенации, например str1 + str2, создает совершенно новую строку в памяти, куда копируются данные из старых строк. В цикле это приводит к множественным аллокациям памяти и копированиям данных, что крайне неэффективно.

strings.Builder решает эту проблему, используя внутренний изменяемый буфер ([]byte). При добавлении строк он дописывает данные в этот буфер, расширяя его по мере необходимости, но делает это гораздо реже, чем при каждой операции. Это минимизирует количество аллокаций.

Пример:

package main

import (
    "fmt"
    "strconv"
    "strings"
)

func main() {
    var builder strings.Builder
    // Можно заранее выделить память, если известен примерный размер
    // builder.Grow(100)

    for i := 0; i < 10; i++ {
        builder.WriteString("item ")
        builder.WriteString(strconv.Itoa(i))
        builder.WriteString(", ")
    }

    result := builder.String() // Финальная строка создается только один раз
    fmt.Println(result)
}

Сравнение с другими способами:

  • Оператор + или +=: Очень неэффективен в циклах. Подходит только для очень малого, известного числа объединений вне циклов.
  • fmt.Sprintf: Удобен для форматирования, но еще медленнее, чем +, из-за парсинга строки формата и использования рефлексии.
  • bytes.Buffer: Похож на strings.Builder, но работает с []byte. Исторически использовался для этих целей до появления Builder. strings.Builder немного эффективнее для сборки именно строк, так как избегает лишних преобразований []byte в string.