Лучше ли использовать String.format вместо конкатенации строк в Java?
-
06-09-2019 - |
Вопрос
Есть ли заметная разница между использованием String.format
а конкатенация строк в Java?
Я склонен использовать String.format
но иногда будет проскальзывать и использоваться конкатенация.Мне было интересно, было ли одно лучше другого.
То, как я это вижу, String.format
дает вам больше возможностей при "форматировании" строки;а конкатенация означает, что вам не нужно беспокоиться о случайном вводе дополнительного %s или его пропуске.
String.format
тоже короче.
Какой из них более читабелен, зависит от того, как работает ваша голова.
Решение
Я бы предположил, что это лучшая практика для использования String.format()
.Основная причина заключается в том, что String.format()
может быть более легко локализован с помощью текста, загруженного из файлов ресурсов, тогда как конкатенация не может быть локализована без создания нового исполняемого файла с другим кодом для каждого языка.
Если вы планируете, чтобы ваше приложение было локализуемым, вам также следует выработать привычку указывать позиции аргументов и для ваших токенов формата:
"Hello %1$s the time is %2$t"
Затем это можно локализовать и поменять местами маркеры имени и времени, не требуя перекомпиляции исполняемого файла для учета другого порядка.С позициями аргументов вы также можете повторно использовать один и тот же аргумент, не передавая его в функцию дважды:
String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)
Другие советы
О производительности:
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = String.format("Hi %s; Hi to you %s",i, + i*2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
}
Временные результаты следующие:
- Конкатенация = 265 миллисекунд
- Формат = 4141 миллисекунда
Таким образом, конкатенация выполняется намного быстрее, чем String.format.
Поскольку идет дискуссия о производительности, я решил добавить сравнение, включающее StringBuilder.На самом деле это быстрее, чем concat и, естественно, опция String.format.
Чтобы сделать это своего рода сравнением яблок с яблоками, я создаю экземпляр нового StringBuilder в цикле, а не снаружи (на самом деле это быстрее, чем выполнение только одного экземпляра, скорее всего, из-за накладных расходов на перераспределение пространства для добавления цикла в конце одного конструктора).
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
log.info("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
log.info("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("; Hi to you ").append(i * 2);
}
end = System.currentTimeMillis();
log.info("String Builder = " + ((end - start)) + " millisecond");
- 2012-01-11 16:30:46,058 ИНФОРМАЦИЯ [TestMain] - Формат = 1416 миллисекунд
- 2012-01-11 16:30:46,190 ИНФОРМАЦИЯ [TestMain] - Конкатенация = 134 миллисекунды
- 2012-01-11 16:30:46,313 ИНФОРМАЦИЯ [TestMain] - Конструктор строк = 117 миллисекунд
Одна проблема с .format
заключается в том, что вы теряете статическую безопасность типов.У вас может быть слишком мало аргументов для вашего формата, и у вас могут быть неправильные типы для спецификаторов формата - и то, и другое приводит к IllegalFormatException
во время выполнения, таким образом, вы можете в конечном итоге получить код протоколирования, который прерывает производство.
Напротив, аргументы в пользу +
может быть протестирован компилятором.
Какой из них более читабелен, зависит от того, как работает ваша голова.
Вы получили свой ответ прямо здесь.
Это вопрос личного вкуса.
Я полагаю, конкатенация строк происходит незначительно быстрее, но это должно быть незначительно.
Вот тест с несколькими размерами выборки в миллисекундах.
public class Time {
public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;
public static void main(String[] args) {
int i = 1;
for(int run=1; run <= 12; run++){
for(int test =1; test <= 2 ; test++){
System.out.println(
String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
test(run, i);
}
System.out.println("\n____________________________");
i = i*3;
}
}
public static void test(int run, int iterations){
long start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = "echo " + i + " > "+ sysFile;
}
long t = System.nanoTime() - start;
String r = String.format(" %-13s =%10d %s", "Concatenation",t,"nanosecond");
System.out.println(r) ;
start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = String.format(cmdString, i);
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "Format",t,"nanosecond");
System.out.println(r);
start = System.nanoTime();
for( int i=0;i<iterations; i++){
StringBuilder b = new StringBuilder("echo ");
b.append(i).append(" > ").append(sysFile);
String s = b.toString();
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "StringBuilder",t,"nanosecond");
System.out.println(r);
}
}
TEST: 1, RUN: 1, Iterations: 1
Concatenation = 14911 nanosecond
Format = 45026 nanosecond
StringBuilder = 3509 nanosecond
TEST: 1, RUN: 2, Iterations: 1
Concatenation = 3509 nanosecond
Format = 38594 nanosecond
StringBuilder = 3509 nanosecond
____________________________
TEST: 2, RUN: 1, Iterations: 3
Concatenation = 8479 nanosecond
Format = 94438 nanosecond
StringBuilder = 5263 nanosecond
TEST: 2, RUN: 2, Iterations: 3
Concatenation = 4970 nanosecond
Format = 92976 nanosecond
StringBuilder = 5848 nanosecond
____________________________
TEST: 3, RUN: 1, Iterations: 9
Concatenation = 11403 nanosecond
Format = 287115 nanosecond
StringBuilder = 14326 nanosecond
TEST: 3, RUN: 2, Iterations: 9
Concatenation = 12280 nanosecond
Format = 209051 nanosecond
StringBuilder = 11818 nanosecond
____________________________
TEST: 5, RUN: 1, Iterations: 81
Concatenation = 54383 nanosecond
Format = 1503113 nanosecond
StringBuilder = 40056 nanosecond
TEST: 5, RUN: 2, Iterations: 81
Concatenation = 44149 nanosecond
Format = 1264241 nanosecond
StringBuilder = 34208 nanosecond
____________________________
TEST: 6, RUN: 1, Iterations: 243
Concatenation = 76018 nanosecond
Format = 3210891 nanosecond
StringBuilder = 76603 nanosecond
TEST: 6, RUN: 2, Iterations: 243
Concatenation = 91222 nanosecond
Format = 2716773 nanosecond
StringBuilder = 73972 nanosecond
____________________________
TEST: 8, RUN: 1, Iterations: 2187
Concatenation = 527450 nanosecond
Format = 10291108 nanosecond
StringBuilder = 885027 nanosecond
TEST: 8, RUN: 2, Iterations: 2187
Concatenation = 526865 nanosecond
Format = 6294307 nanosecond
StringBuilder = 591773 nanosecond
____________________________
TEST: 10, RUN: 1, Iterations: 19683
Concatenation = 4592961 nanosecond
Format = 60114307 nanosecond
StringBuilder = 2129387 nanosecond
TEST: 10, RUN: 2, Iterations: 19683
Concatenation = 1850166 nanosecond
Format = 35940524 nanosecond
StringBuilder = 1885544 nanosecond
____________________________
TEST: 12, RUN: 1, Iterations: 177147
Concatenation = 26847286 nanosecond
Format = 126332877 nanosecond
StringBuilder = 17578914 nanosecond
TEST: 12, RUN: 2, Iterations: 177147
Concatenation = 24405056 nanosecond
Format = 129707207 nanosecond
StringBuilder = 12253840 nanosecond
Вот тот же тест, что и выше, с модификацией вызова toString() метод на основе Строковый конструктор.Приведенные ниже результаты показывают, что подход StringBuilder работает лишь немного медленнее, чем конкатенация строк с использованием + оператор.
файл:StringTest.java
class StringTest {
public static void main(String[] args) {
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("Hi to you ").append(i * 2).toString();
}
end = System.currentTimeMillis();
System.out.println("String Builder = " + ((end - start)) + " millisecond");
}
}
Команды оболочки :(скомпилируйте и запустите StringTest 5 раз)
> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"
Результаты :
Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond
Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
String.format()
это больше, чем просто объединение строк.Например, вы можете отображать числа в определенном языковом стандарте, используя String.format()
.
Однако, если вас не волнует локализация, функциональной разницы нет.Возможно, одно работает быстрее другого, но в большинстве случаев это будет незначительно..
Как правило, конкатенации строк следует отдавать предпочтение перед String.format
.Последнее имеет два основных недостатка:
- Он не кодирует строку, которая должна быть построена локальным образом.
- Процесс построения закодирован в виде строки.
Под пунктом 1 я подразумеваю, что невозможно понять, что такое String.format()
вызов выполняется за один последовательный проход.Человек вынужден переходить взад и вперед между строкой формата и аргументами, одновременно подсчитывая положение аргументов.Для коротких конкатенаций это не является большой проблемой.Однако в этих случаях конкатенация строк менее подробна.
Под пунктом 2 я подразумеваю, что важная часть процесса построения закодирована в форматировать строку (используя DSL).Использование строк для представления кода имеет много недостатков.Он по своей сути не является типобезопасным и усложняет подсветку синтаксиса, анализ кода, оптимизацию и т.д.
Конечно, при использовании инструментов или фреймворков, внешних по отношению к языку Java, могут вступать в игру новые факторы.
Я не проводил никаких конкретных тестов, но я бы подумал, что конкатенация может быть быстрее.String.format() создает новый форматировщик, который, в свою очередь, создает новый StringBuilder (размером всего 16 символов).Это изрядные накладные расходы, особенно если вы форматируете более длинную строку, а StringBuilder постоянно приходится изменять размер.
Однако конкатенация менее полезна и ее труднее читать.Как всегда, стоит провести бенчмарк для вашего кода, чтобы понять, какой из них лучше.Различия могут быть незначительными в серверном приложении после того, как ваши пакеты ресурсов, локали и т.д. загружены в память и код изменен.
Возможно, в качестве наилучшей практики было бы неплохо создать свой собственный форматировщик с соответствующим размером StringBuilder (Appendable) и Locale и использовать его, если вам нужно выполнить много форматирования.
Здесь может быть заметная разница.
String.format
он довольно сложный и использует регулярное выражение внизу, поэтому не берите за привычку использовать его везде, а только там, где вам это нужно.
StringBuilder
было бы на порядок быстрее (как кто-то здесь уже указывал).
Вы не можете сравнивать конкатенацию строк и String.Форматирование с помощью программы, описанной выше.
Вы можете попробовать, что это также меняет позицию использования вашей строки.Форматируйте и конкатенируйте в вашем блоке кода, как показано ниже
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}
Вы будете удивлены, увидев, что этот Формат работает здесь быстрее.Это связано с тем, что созданные исходные объекты могут не быть выпущены, и может возникнуть проблема с выделением памяти и, следовательно, с производительностью.
Требуется немного времени, чтобы привыкнуть к Струнам.Формат, но в большинстве случаев оно того стоит.В мире NRA (никогда ничего не повторяйте) чрезвычайно полезно хранить ваши токенизированные сообщения (logging или user) в постоянной библиотеке (я предпочитаю то, что соответствует статическому классу) и вызывать их по мере необходимости с помощью String .Форматируйте независимо от того, выполняете ли вы локализацию или нет.Пытаться использовать такую библиотеку с методом конкатенации сложнее для чтения, устранения неполадок, корректуры и управления любым подходом, требующим конкатенации.Замена - это вариант, но я сомневаюсь, что он эффективен.После многих лет использования моя самая большая проблема связана со строкой.Формат - это длина вызова, которая неудобно велика, когда я передаю ее в другую функцию (например, Msg), но это легко обойти с помощью пользовательской функции, которая служит псевдонимом.
Я думаю, мы можем пойти с MessageFormat.format
поскольку он должен быть хорош как с точки зрения удобочитаемости, так и с точки зрения производительности.
Я использовал ту же программу, которую использовал Икаро в своем приведенном выше ответе и я улучшил его, добавив код для использования MessageFormat
чтобы объяснить показатели производительности.
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
}
Конкатенация = 69 миллисекунд
Формат = 1435 миллисекунд .
Формат сообщения = 200 миллисекунд
Обновления:
Согласно отчету SonarLint, строки формата в стиле Printf должны использоваться правильно (squid: S3457)
Поскольку строки формата в стиле printf интерпретируются во время выполнения, а не проверяются компилятором, они могут содержать ошибки, которые приводят к созданию неправильных строк.Это правило статически проверяет соответствие строк формата в стиле printf их аргументам при вызове методов format(...) java.util.Formatter, java.lang.Классы String, java.io.PrintStream, MessageFormat и java.io.PrintWriter, а также методы printf (...) классов java.io.PrintStream или java.io.PrintWriter.
Я заменил стиль printf фигурными скобками и получил кое-что интересное, как показано ниже.
Конкатенация = 69 миллисекунд
Формат = 1107 миллисекунд .
Формат: фигурные скобки = 416 миллисекунд
Формат сообщения = 215 миллисекунда
Формат сообщения: фигурные скобки = 2517 миллисекунд
Мой Вывод:
Как я подчеркивал выше, использование String.format с фигурными скобками должно быть хорошим выбором для получения преимуществ хорошей читаемости, а также производительности.