Мы должны строить строки все время для вывода журнала и так далее. В версиях JDK мы узнали, когда использовать StringBuffer
(многие добавления, поточно-ориентированные) и StringBuilder
(многие добавления, не поточнобезопасные).
Какой совет по использованию String.format()
? Это эффективно, или мы вынуждены придерживаться конкатенации для однострочников, где важна производительность?
например, уродливый старый стиль,
String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";
против аккуратного нового стиля (String.format, который возможно медленнее),
String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);
Примечание: мой конкретный вариант использования - это сотни строк журнала «с одной строкой» в моем коде. Они не содержат петли, поэтому StringBuilder
слишком тяжелые. Меня интересует String.format()
конкретно.
Ответы:
Я написал небольшой класс для тестирования, который имеет лучшую производительность, чем два, и + опережает формат. в 5-6 раз. Попробуйте сами
Выполнение вышеуказанного для разных N показывает, что оба ведут себя линейно, но
String.format
медленнее в 5-30 раз.Причина в том, что в текущей реализации
String.format
сначала анализируются входные данные с помощью регулярных выражений, а затем заполняются параметры. С другой стороны, конкатенация с плюсом оптимизируется с помощью javac (а не JIT) и используетсяStringBuilder.append
напрямую.источник
Я взял код hhafez и добавил тест памяти :
Я запускаю это отдельно для каждого подхода, оператора '+', String.format и StringBuilder (вызывая toString ()), поэтому другие подходы не влияют на используемую память. Я добавил еще несколько конкатенаций, сделав строку «Бла» + я + «Бла» + я + «Бла» + я + «Бла».
Результат выглядит следующим образом (в среднем по 5 прогонов каждый):
Время подхода (мс)
Оператор выделения памяти (длинный) Оператор «+» 747 320
504 String.format 16484 373 312 StringBuilder
769 57 344
Мы видим, что String '+' и StringBuilder практически идентичны по времени, но StringBuilder намного более эффективен в использовании памяти. Это очень важно, когда у нас много вызовов журнала (или любых других операторов, включающих строки) за достаточно короткий промежуток времени, поэтому сборщик мусора не сможет очистить множество строковых экземпляров, полученных в результате оператора '+'.
И заметьте, кстати, не забудьте проверить уровень ведения журнала перед построением сообщения.
Выводы:
источник
+
оператора компилируется в эквивалентныйStringBuilder
код. Подобные микробенчмарки не являются хорошим способом измерения производительности - почему бы не использовать jvisualvm, он есть в jdk по определенной причине.String.format()
будет медленнее, но из-за времени для разбора строки формата, а не из-за каких-либо распределений объектов. Откладывать создание артефактов регистрации до тех пор, пока вы не будете уверены, что они нужны, - хороший совет, но если это скажется на производительности, это не в том месте.And a note, BTW, don't forget to check the logging level before constructing the message.
это не хороший совет. Предполагая, что мы говоримjava.util.logging.*
конкретно, проверка уровня ведения журнала - это когда вы говорите о выполнении расширенной обработки, которая может вызвать неблагоприятные последствия для программы, чего вы не хотели бы, если в программе не было включено ведение журнала на соответствующем уровне. Форматирование строк - не тот тип обработки ВСЕ. Форматирование является частьюjava.util.logging
структуры, и сам регистратор проверяет уровень ведения журнала до того, как форматировщик когда-либо будет вызван.Все представленные здесь тесты имеют некоторые недостатки , поэтому результаты не являются надежными.
Я был удивлен, что никто не использовал JMH для бенчмаркинга, поэтому я и сделал.
Полученные результаты:
Единицы - это операции в секунду, чем больше, тем лучше. Исходный код теста . Использовалась OpenJDK IcedTea 2.5.4 Java Virtual Machine.
Итак, старый стиль (использование +) намного быстрее.
источник
Ваш старый уродливый стиль автоматически компилируется JAVAC 1.6 как:
Таким образом, нет абсолютно никакой разницы между этим и использованием StringBuilder.
String.format намного тяжелее, поскольку он создает новый Formatter, анализирует строку входного формата, создает StringBuilder, добавляет к нему все и вызывает toString ().
источник
+
иStringBuilder
действительно. К сожалению, есть много дезинформации в других ответах в этой теме. Я почти соблазн изменить вопрос наhow should I not be measuring performance
.Java String.format работает так:
если конечным пунктом назначения для этих данных является поток (например, рендеринг веб-страницы или запись в файл), вы можете собрать фрагменты формата непосредственно в свой поток:
Я предполагаю, что оптимизатор оптимизирует обработку строки формата. Если это так, у вас останется эквивалентная амортизированная производительность для ручного развертывания вашего String.format в StringBuilder.
источник
String.format
внутренних циклов (выполняемых миллионы раз) приводит к более чем 10% моего времени выполненияjava.util.Formatter.parse(String)
. Кажется, это указывает на то, что во внутренних циклах вам следует избегать вызоваFormatter.format
или чего-либо, что вызывает его, в том числеPrintStream.format
(недостаток стандартной библиотеки Java, IMO, тем более что вы не можете кэшировать проанализированную строку формата).Чтобы развернуть / исправить первый ответ выше, String.format не поможет в переводе.
String.format поможет вам при печати даты / времени (или числового формата и т. Д.), Где существуют различия в локализации (l10n) (т. Е. Некоторые страны будут печатать 04Feb2009, а другие - фев042009).
При переводе вы просто говорите о перемещении любых внешних строк (например, сообщений об ошибках и чего-то еще) в пакет свойств, чтобы вы могли использовать правильный пакет для нужного языка, используя ResourceBundle и MessageFormat.
Глядя на все вышесказанное, я бы сказал, что с точки зрения производительности, String.format и простой конкатенации сводится к тому, что вы предпочитаете. Если вы предпочитаете смотреть на вызовы .format, а не на конкатенацию, то обязательно используйте это.
В конце концов, код читается намного больше, чем написано.
источник
В вашем примере производительность Probalby не слишком отличается, но есть и другие вопросы, которые следует учитывать, а именно: фрагментация памяти. Даже операция конкатенации создает новую строку, даже если она временная (для ее сборки требуется время, и это требует больше работы). String.format () просто более читабелен и требует меньше фрагментации.
Кроме того, если вы часто используете определенный формат, не забывайте, что вы можете использовать класс Formatter () напрямую (все, что делает String.format () - это создание экземпляра одноразового Formatter).
Кроме того, еще кое-что, о чем вы должны знать: будьте осторожны с использованием substring (). Например:
Эта большая строка все еще находится в памяти, потому что именно так работают подстроки Java. Лучшая версия:
или
Вторая форма, вероятно, более полезна, если вы делаете другие вещи одновременно.
источник
Как правило, вы должны использовать String.Format, потому что он относительно быстрый и поддерживает глобализацию (при условии, что вы на самом деле пытаетесь написать что-то, что читается пользователем). Это также облегчает глобализацию, если вы пытаетесь перевести одну строку вместо 3 или более на оператор (особенно для языков, которые имеют резко отличающиеся грамматические структуры).
Теперь, если вы никогда не планируете переводить что-либо, тогда либо полагайтесь на встроенную в Java конвертацию операторов + в
StringBuilder
. Или используйте JavaStringBuilder
явно.источник
Еще одна перспектива только с точки зрения ведения журнала.
Я вижу много дискуссий, связанных с входом в эту ветку, поэтому я подумал добавить свой опыт в ответ. Может быть, кто-то найдет это полезным.
Я полагаю, что мотивация ведения журнала с использованием форматера заключается в том, чтобы избежать объединения строк. По сути, вы не хотите иметь издержки на строку concat, если вы не собираетесь ее регистрировать.
Вам на самом деле не нужно выполнять concat / format, если вы не хотите войти. Скажем, если я определю такой метод
При таком подходе cancat / formatter на самом деле не вызывается вообще, если это сообщение отладки и debugOn = false
Хотя все равно будет лучше использовать StringBuilder вместо форматера. Основная мотивация - избегать всего этого.
В то же время я не люблю добавлять блок «if» для каждого оператора регистрации, так как
Поэтому я предпочитаю создавать класс утилиты ведения журнала с помощью методов, описанных выше, и использовать его везде, не беспокоясь о падении производительности и любых других проблемах, связанных с ним.
источник
Я только что изменил тест Хафеза, чтобы включить StringBuilder. StringBuilder в 33 раза быстрее, чем String.format с использованием клиента jdk 1.6.0_10 в XP. Использование ключа -server снижает коэффициент до 20.
Хотя это может звучать радикально, я считаю, что это актуально только в редких случаях, потому что абсолютные числа довольно малы: 4 с на 1 миллион простых вызовов String.format вроде как - до тех пор, пока я использую их для регистрации или лайк.
Обновление: как отмечено в комментариях sjbotha, тест StringBuilder недопустим, так как в нем отсутствует финал
.toString()
.Правильный коэффициент ускорения с
String.format(.)
доStringBuilder
составляет 23 на моей машине (16 с-server
переключателем).источник
Вот измененная версия записи hhafez. Он включает в себя параметр строителя строк.
}
Время после для цикла 391 Время после для цикла 4163 Время после для цикла 227
источник
Ответ на этот вопрос во многом зависит от того, как ваш конкретный компилятор Java оптимизирует генерируемый им байт-код. Строки являются неизменяемыми, и теоретически каждая операция «+» может создавать новую. Но ваш компилятор почти наверняка оптимизирует промежуточные этапы построения длинных строк. Вполне возможно, что обе строки кода выше генерируют один и тот же байт-код.
Единственный реальный способ узнать это итеративно тестировать код в текущей среде. Напишите приложение QD, которое объединяет строки в обе стороны итеративно, и посмотрите, как они выдерживают время друг против друга.
источник
Рассмотрите возможность использования
"hello".concat( "world!" )
небольшого числа строк в конкатенации. Это может быть даже лучше для производительности, чем другие подходы.Если у вас более 3 строк, подумайте об использовании StringBuilder или просто String, в зависимости от используемого компилятора.
источник