Используйте strings.Builder и strconv.Itoa вместо многократного копирования накопленной строки через +=. Пример улучшения в условии уже реализует этот подход.
Эффективное формирование строки в цикле
Используйте strings.Builder и strconv.Itoa вместо многократного копирования накопленной строки через +=. Пример улучшения в условии уже реализует этот подход.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В данном примере рассматриваются типичные проблемы при построении строки в цикле и способы их устранения.
func fn() {
result := ""
for i := 0; i < 100000; i++ {
result += fmt.Sprintf("%d", i)
}
}
Проблемы:
- Операция
result += …каждый раз создает новую строку, копируя содержимое, что приводит к O(n²) времени и большому расходу памяти. - Внутри цикла используется
fmt.Sprintf, который тоже дорогой, так как формирует строку через форматирование.
Рекомендации по улучшению:
- Использовать
strings.Builder(илиbytes.Buffer) – он умеет аккумулировать байты без повторного копирования. - Преобразовывать число в строку без
Sprintf, например черезstrconv.Itoa.
func fn() {
var b strings.Builder
b.Grow(600000) // приблизительная оценка размера, чтобы избежать перераспределений
for i := 0; i < 100000; i++ {
b.WriteString(strconv.Itoa(i))
}
result := b.String()
_ = result // использовать дальше
}
Такой подход уменьшает время выполнения и потребление памяти до O(n).
Ответ
Используйте strings.Builder и strconv.Itoa вместо многократного копирования накопленной строки через +=. Пример улучшения в условии уже реализует этот подход.
Стоимость сборки становится линейной по объёму итоговых символов при нормальном росте буфера. У исходного варианта сумма повторных копирований быстро растёт; Sprintf добавляет расходы универсального форматирования. Grow можно оценить заранее, но это не обязательное точное значение. Для переменного n длина десятичной записи тоже растёт, поэтому «O(n)» лучше уточнять как O(числа выходных байтов).