Конкатенация строк
1. Базовый синтаксис
Оператор +
hello := "Hello"
world := "World"
greeting := hello + ", " + world + "!"
fmt.Println(greeting) // "Hello, World!"
Работает интуитивно — складываем строки как числа.
Сокращённая форма +=
message := "Go"
message += " is"
message += " awesome!"
fmt.Println(message) // "Go is awesome!"
Аналог: message = message + " is".
2. Проблема: сколько строк создаётся?
Вопрос из материала
greeting := hello + ", " + world + "!"
Кажется: 3 строки (hello, world, greeting).
На самом деле: 6 строк!
Детальный разбор
hello := "Hello" // 1. Строка "Hello"
world := "World" // 2. Строка "World"
greeting := hello + ", " + world + "!"
Пошаговое выполнение:
| Шаг | Операция | Результат | Строка в памяти |
|---|---|---|---|
| 1 | hello |
"Hello" |
Строка #1 |
| 2 | world |
"World" |
Строка #2 |
| 3 | ", " |
", " |
Строка #3 (константа) |
| 4 | hello + ", " |
"Hello, " |
Строка #4 (временная) |
| 5 | ... + world |
"Hello, World" |
Строка #5 (временная) |
| 6 | "!" |
"!" |
Строка #6 (константа) |
| 7 | ... + "!" |
"Hello, World!" |
Строка #7 (финальная) |
Итого: 7 строк (включая константы и временные).
3. Почему это дорого
Механизм конкатенации
При каждом + происходит:
- Выделение новой памяти под результат
- Копирование байтов левой строки
- Копирование байтов правой строки
- Создание новой структуры
StringHeader
"Hello" + ", " + "World"
↓
Выделить память (8 байт)
Скопировать "Hello" (5 байт)
Скопировать ", " (2 байт)
→ "Hello, " (временная строка)
↓
Выделить память (12 байт)
Скопировать "Hello, " (7 байт)
Скопировать "World" (5 байт)
→ "Hello, World"
Проблема: Каждая промежуточная строка создаётся в памяти.
4. Когда конкатенация приемлема
Небольшое количество операций
name := "Alice"
greeting := "Hello, " + name + "!" // ✅ OK
Допустимо: 2-3 операции +, редкие вызовы.
5. Когда конкатенация проблематична
Циклы и большие объёмы
// ❌ ПЛОХО: Количество операций стремится к n²
result := ""
for i := 0; i < 1000000; i++ {
result += "a" // Каждый раз новая строка!
}
Почему плохо:
- 1-я итерация: выделить 1 байт
- 2-я итерация: выделить 2 байта, скопировать 1
- 3-я итерация: выделить 3 байта, скопировать 2
- …
- n-я итерация: выделить n байт, скопировать n-1
6. Альтернативы (предварительно)
Из материала:
“Нужно использовать какие-то другие механизмы и инструменты”.
Для циклов: strings.Builder
var builder strings.Builder
for i := 0; i < 1000000; i++ {
builder.WriteString("a")
}
result := builder.String()
Преимущество: память выделяется один раз с запасом.
Для известного количества: strings.Join
parts := []string{"Hello", "World", "!"}
result := strings.Join(parts, ", ")
fmt.Println(result) // "Hello, World, !"
7. Примеры неэффективности
Плохо: цикл с +=
s := ""
for i := 0; i < 10000; i++ {
s += fmt.Sprintf("Item %d\n", i) // ❌ Каждый раз новая строка
}
Хорошо: strings.Builder
var builder strings.Builder
for i := 0; i < 10000; i++ {
builder.WriteString(fmt.Sprintf("Item %d\n", i))
}
s := builder.String()
8. Сравнение производительности
| Метод | 1000 строк | 100,000 строк |
|---|---|---|
+= |
~1ms | ~5000ms |
strings.Builder |
~0.5ms | ~10ms |
Разница: На больших объёмах — в сотни раз.
9. Правило большого пальца
✅ Используйте +:
- Однократная конкатенация
- 2-3 операции
- Статические строки
❌ Не используйте +:
- Циклы
- Сотни/тысячи операций
- Динамическое построение
10. Итоги
- ✅ Оператор
+работает для строк - ✅
+=— сокращённая форма - ❌ Каждый
+создаёт новую строку в памяти - ❌ В выражении
a + b + c + dсоздаётся много временных строк - ❌ Конкатенация в циклах — много операций
- ✅ Для больших объёмов —
strings.Builderилиstrings.Join - ✅ На малых объёмах
+приемлем
Ключевое правило:
Простая конкатенация: a + b + c → OK для 2-3 строк
Цикл с конкатенацией: s += x → ❌ Используйте Builder
Сотни тысяч операций: builder.Write → очень быстрая работа
Практический совет:
// Плохо для больших N
for i := 0; i < N; i++ {
result += str // медленно
}
// Хорошо для любых N
var b strings.Builder
for i := 0; i < N; i++ {
b.WriteString(str) // быстро
}
result := b.String()