Лучше ли использовать String.format вместо конкатенации строк в Java?
Есть ли заметная разница между использованием String.Format
и конкатенация строк в Java?
Я склонен использовать String.format
но иногда будет скользить и использовать Concat. Мне было интересно, если один был лучше, чем другой.
Как я вижу это, String.Format
дает вам больше возможностей в "форматировании" строки; и конкатенация означает, что вам не нужно беспокоиться о случайном добавлении%s или об отсутствии.
String.format
тоже короче.
Какой из них более читабелен, зависит от того, как работает ваша голова.
16 ответов
Я бы предположил, что лучше использовать 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.
Одна проблема с .format
является то, что вы теряете безопасность статического типа. У вас может быть слишком мало аргументов для вашего формата, и у вас могут быть неправильные типы для спецификаторов формата - оба приводят к IllegalFormatException
во время выполнения, так что вы можете в конечном итоге с регистрацией кода, который нарушает работу.
Напротив, аргументы +
может быть проверено компилятором.
Так как есть обсуждение производительности, я решил добавить сравнение с 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 INFO [TestMain] - Формат = 1416 миллисекунд
- 2012-01-11 16:30:46,990 INFO [TestMain] - сцепление = 134 миллисекунды
- 2012-01-11 16:30:46,313 ИНФОРМАЦИЯ [TestMain] - String Builder = 117 миллисекунд
Какой из них более читабелен, зависит от того, как работает ваша голова.
Вы получили свой ответ прямо там.
Это вопрос личного вкуса.
Полагаю, что сцепление строк незначительно быстрее, но это должно быть незначительным.
Вот тест с несколькими размерами выборки в миллисекундах.
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. Приведенные ниже результаты показывают, что подход 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, могут вступать в действие новые факторы.
Многократное повторение неправильного теста Вы должны использовать {} no %s .
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("Wrong use of the message format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi {0}; Hi to you {1}", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Good use of the message format = " + ((end - start)) + " millisecond");
}
Concatenation = 88 millisecond
Wrong use of the message format = 1075 millisecond
Good use of the message format = 376 millisecond
Я не делал каких-либо конкретных тестов, но я думаю, что объединение может быть быстрее. String.format() создает новый Formatter, который, в свою очередь, создает новый StringBuilder (с размером только 16 символов). Это значительный объем накладных расходов, особенно если вы форматируете более длинную строку, а StringBuilder продолжает изменять размер.
Однако конкатенация менее полезна и труднее для чтения. Как всегда, стоит сделать тест на вашем коде, чтобы увидеть, что лучше. Различия могут быть незначительными в серверном приложении после того, как пакеты ресурсов, локали и т. Д. Загружены в память и код JITted.
Возможно, в качестве лучшей практики было бы неплохо создать собственный форматтер с правильно подобранным StringBuilder (Appendable) и Locale и использовать его, если вам нужно много форматирования.
Там может быть ощутимая разница.
String.format
довольно сложный и использует регулярное выражение внизу, так что не делайте это привычкой использовать его везде, но только там, где вам это нужно.
StringBuilder
будет на порядок быстрее (как кто-то здесь уже указал).
Я думаю, что мы можем пойти с MessageFormat.format
как это должно быть хорошо как с точки зрения читаемости, так и с точки зрения производительности.
Я использовал ту же программу, которую использовал Icaro в своем ответе выше, и я добавил в нее дополнительный код для использования 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 миллисекунд
MessageFormat = 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 миллисекунд
MessageFormat = 215 миллисекунд
MessageFormat: фигурные скобки = 2517 миллисекунд
Мой вывод:
Как я подчеркивал выше, использование String.format с фигурными скобками должно быть хорошим выбором для получения преимуществ хорошей читаемости, а также производительности.
Требуется немного времени, чтобы привыкнуть к String.Format, но в большинстве случаев оно того стоит. В мире NRA (никогда ничего не повторяйте) чрезвычайно полезно хранить ваши токенизированные сообщения (ведение журнала или пользователя) в библиотеке констант (я предпочитаю то, что равно статическому классу) и вызывать их при необходимости с помощью String.Format независимо от того, используете ли вы локализуются или нет. Попытки использовать такую библиотеку с методом конкатенации труднее читать, устранять неполадки, корректировать и управлять любым подходом, требующим конкатенации. Замена - это вариант, но я сомневаюсь, что он эффективен. После многих лет использования моя самая большая проблема с String.Format заключается в том, что длительность вызова неудобно велика, когда я передаю его в другую функцию (например, Msg), но это легко обойти с помощью пользовательской функции, служащей псевдонимом.,
Формат = 1508 миллисекунд. Конкатенация = 31 миллисекунда.
Процесс завершен с кодом завершения 0
Concat намного лучше, чем формат.
Вы не можете сравнить String Concatenation и String.Format с помощью указанной выше программы.
Вы можете попробовать это также поменять местами использование ваших String.Format и Concatenation в вашем блоке кода, как показано ниже
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") ;
}
Вы будете удивлены, увидев, что Формат работает здесь быстрее. Это связано с тем, что созданные объекты могут быть не выпущены, и может возникнуть проблема с выделением памяти и, следовательно, с производительностью.