Лучше ли использовать 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. Он не кодирует строку, которая будет построена локально.
  2. Процесс сборки кодируется в виде строки.

Под пунктом 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") ;
}

Вы будете удивлены, увидев, что Формат работает здесь быстрее. Это связано с тем, что созданные объекты могут быть не выпущены, и может возникнуть проблема с выделением памяти и, следовательно, с производительностью.

Другие вопросы по тегам