В Go, a string
является примитивным типом, что означает, что он доступен только для чтения, и каждая его манипуляция создаст новую строку.
Итак, если я хочу объединить строки много раз, не зная длины полученной строки, каков наилучший способ сделать это?
Наивным способом будет:
s := ""
for i := 0; i < 1000; i++ {
s += getShortStringFromSomewhere()
}
return s
но это не кажется очень эффективным.
string
go
string-concatenation
Рэнди Сугианто 'Юку'
источник
источник
append()
пришли на язык, что является хорошим решением для этого. Он будет работать так же быстро,copy()
но сначала будет увеличивать срез, даже если это означает выделение нового резервного массива, если емкости недостаточно.bytes.Buffer
по-прежнему имеет смысл, если вам нужны дополнительные удобные методы или ожидаемый пакет, который вы используете.1 + 2 + 3 + 4 + ...
. Этоn*(n+1)/2
площадь треугольника основанияn
. Вы добавляете размер 1, затем размер 2, затем размер 3 и т. Д., Когда добавляете неизменные строки в цикле. Это квадратичное потребление ресурсов проявляется не только в этом.Ответы:
Новый путь:
С Go 1.10 есть
strings.Builder
тип, пожалуйста, посмотрите на этот ответ для более подробной информации .Старый Путь:
Используйте
bytes
пакет. У него естьBuffer
тип, который реализуетio.Writer
.Это происходит за O (n) раз.
источник
buffer := bytes.NewBufferString("")
, вы можете сделатьvar buffer bytes.Buffer
. Вам также не нужны эти точки с запятой :).Наиболее эффективный способ объединения строк - использование встроенной функции
copy
. В моих тестах этот подход примерно в 3 раза быстрее, чем при использовании,bytes.Buffer
и намного быстрее (~ 12 000 раз), чем при использовании оператора+
. Кроме того, он использует меньше памяти.Я создал контрольный пример, чтобы доказать это, и вот результаты:
Ниже приведен код для тестирования:
источник
buffer.Write
(в байтах) на 30% быстрее, чемbuffer.WriteString
. [полезно, если вы можете получить данные как[]byte
]b.N
, и поэтому вы не сравниваете время выполнения одной и той же задачи, которую нужно выполнить (например, одна функция может добавлять1,000
строки, другая может добавлять,10,000
что может иметь большое значение в среднем время 1 добавления,BenchmarkConcat()
например). Вы должны использовать один и тот же счетчик добавлений в каждом случае (конечно, нетb.N
) и выполнять всю конкатенацию внутри телаfor
ранжирования доb.N
(то есть, 2for
встроенных цикла ).В Go 1.10+ есть
strings.Builder
, здесь .пример
Это почти то же самое с
bytes.Buffer
.Нажмите, чтобы увидеть это на детской площадке .
Запись
Поддерживаемые интерфейсы
Методы StringBuilder реализуются с учетом существующих интерфейсов. Так что вы можете легко переключаться на новый тип Builder в своем коде.
Отличия от байтов. Буфер
Он может только расти или сбрасываться.
Он имеет встроенный механизм copyCheck, который предотвращает случайное копирование:
func (b *Builder) copyCheck() { ... }
В
bytes.Buffer
, можно получить доступ основных данных , например , следующим образом :(*Buffer).Bytes()
.strings.Builder
предотвращает эту проблему.io.Reader
т. Д.Проверьте его исходный код для более подробной информации, здесь .
источник
strings.Builder
реализует свои методы, используя указатель приемника, который бросил меня на мгновение. В результате я, вероятно, создал бы один, используяnew
.В пакете строк есть библиотечная функция
Join
: http://golang.org/pkg/strings/#JoinВзгляд на код
Join
показывает аналогичный подход к функции добавления. Кинопико пишет: https://golang.org/src/strings/strings.go#L420.Применение:
источник
Я только что проверил верхний ответ, опубликованный выше, в своем собственном коде (рекурсивное обход дерева) и простой оператор concat на самом деле быстрее, чем
BufferString
.Это заняло 0,81 секунды, тогда как следующий код:
заняло всего 0,61 секунды. Вероятно, это связано с накладными расходами на создание нового
BufferString
.Обновление: я также протестировал
join
функцию, и она запустилась за 0,54 секунды.источник
buffer.WriteString("\t");
buffer.WriteString(subs[i]);
(strings.Join)
бега - самый быстрый, потому что это говорит о том, что(bytes.Buffer)
он победитель!Вы можете создать большой кусок байтов и скопировать байты коротких строк в него, используя строковые фрагменты. В «Effective Go» есть функция:
Затем, когда операции завершены, используйте
string ( )
большой кусок байтов, чтобы снова преобразовать его в строку.источник
append(slice, byte...)
, кажется.Это самое быстрое решение, которое не требует, чтобы вы сначала знали или рассчитали общий размер буфера:
По моим оценкам , это на 20% медленнее, чем решение для копирования (8,1 нс на приложение, а не 6,72 нс), но все еще на 55% быстрее, чем использование байтов. Буфер.
источник
источник
Примечание добавлено в 2018 году
С Go 1.10 есть
strings.Builder
тип, пожалуйста, посмотрите на этот ответ для более подробной информации .Ответ до 201x
Код теста @ cd1 и другие ответы неверны.
b.N
не должен быть установлен в тестовой функции. Он устанавливается инструментом динамического тестирования, чтобы определить, стабильно ли время выполнения теста.Функция эталонного теста должна запускать одинаковое
b.N
время теста, и тест внутри цикла должен быть одинаковым для каждой итерации. Поэтому я исправляю это, добавляя внутренний цикл. Я также добавлю тесты для некоторых других решений:Окружающая среда - OS X 10.11.6, 2,2 ГГц Intel Core i7
Результаты теста:
Вывод:
CopyPreAllocate
самый быстрый способ;AppendPreAllocate
довольно близко к № 1, но легче написать код.Concat
имеет очень плохую производительность как по скорости, так и по использованию памяти. Не используйте это.Buffer#Write
иBuffer#WriteString
в основном одинаковы по скорости, вопреки тому, что @ Dani-Br сказал в комментарии. Учитывая,string
что действительно[]byte
в Go, это имеет смысл.Copy
с дополнительным бухгалтерским учетом и другими вещами.Copy
иAppend
использовать размер начальной загрузки 64, такой же, как байты. БуферAppend
использовать больше памяти и выделяет, я думаю, это связано с алгоритмом роста, который он использует. Это не растет память так быстро, как байты.Предложение:
Append
илиAppendPreAllocate
. Это достаточно быстро и просто в использовании.bytes.Buffer
конечно. Вот для чего он предназначен.источник
Мое оригинальное предложение было
Но выше ответ, используя bytes.Buffer - WriteString () является наиболее эффективным способом.
Мое первоначальное предложение использует отражение и переключение типов. Смотрите
(p *pp) doPrint
и(p *pp) printArg
нет универсального интерфейса Stringer () для базовых типов, как я наивно думал.
По крайней мере, Sprint () внутренне использует bytes.Buffer. таким образом
является приемлемым с точки зрения распределения памяти.
=> Сцепление Sprint () может использоваться для быстрого вывода отладочной информации.
=> В противном случае используйте bytes.Buffer ... WriteString
источник
Расширение ответа cd1: вы можете использовать append () вместо copy (). Функция append () обеспечивает более продвинутые предварительные условия, стоит немного больше памяти, но экономит время. Я добавил еще две отметки вверху вашей. Беги локально с
На моем ThinkPad T400s это дает:
источник
Это актуальная версия теста, предоставляемая @ cd1 (
Go 1.8
,linux x86_64
) с исправлениями ошибок, упомянутых @icza и @PickBoy.Bytes.Buffer
только в7
разы быстрее, чем прямая конкатенация строк через+
оператор.Тайминги:
источник
b.N
публичная переменная?b.N
динамическому набору инструмента тестирования , вы получите строки различной длины в разных тестах. Смотрите комментарийgoutils.JoinBetween
источник
Я делаю это, используя следующее: -
источник
источник
результат теста со статистикой выделения памяти. проверьте код теста на github .
использовать строки. Builder для оптимизации производительности.
источник
источник
[]byte(s1)
преобразования. Сравнивая это с другими опубликованными решениями, можете ли вы назвать одно преимущество вашего решения?strings.Join()
из пакета "Струны"Если у вас есть несоответствие типов (например, если вы пытаетесь соединить int и строку), вы делаете RANDOMTYPE (вещь, которую вы хотите изменить)
EX:
Вывод :
источник
strings.Join()
принимает только 2 параметра: срез и разделительstring
.