在Java中使用String.format而不是字符串连接是更好的做法吗?

时间:2009-05-29 10:51:21

标签: java string concatenation string.format

在Java中使用String.format和字符串连接之间是否存在明显差异?

我倾向于使用String.format但偶尔会滑倒并使用连接。我想知道一个人是否比另一个好。

我看到它的方式,String.format为你提供了“格式化”字符串的更多权力;和连接意味着你不必担心意外地增加额外的%s或丢失一个%s。

String.format也更短。

哪一个更具可读性取决于你的头部如何工作。

14 个答案:

答案 0 :(得分:215)

我建议使用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)

答案 1 :(得分:143)

关于表现:

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快得多。

答案 2 :(得分:36)

由于有关于性能的讨论,我想我会添加一个包含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,190 INFO [TestMain] - 连接= 134毫秒
  • 2012-01-11 16:30:46,313 INFO [TestMain] - String Builder = 117毫秒

答案 3 :(得分:33)

.format的一个问题是您失去了静态类型安全性。您的格式参数可能太少,而且格式说明符的类型可能不正确 - 两者都在运行时导致IllegalFormatException ,因此您可能最终得到的日志代码打破生产。

相反,+的参数可以由编译器测试。

答案 4 :(得分:16)

  

哪一个更具可读性取决于你的头部如何工作。

你在那里得到了答案。

这是个人品味的问题。

我想,字符串连接速度稍快,但这应该可以忽略不计。

答案 5 :(得分:14)

这是一个多个样本大小的测试,以毫秒为单位。

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

答案 6 :(得分:9)

这是与上面相同的测试,修改了在 StringBuilder 上调用 toString() 方法。下面的结果表明StringBuilder方法比使用 + 运算符的字符串连接慢一点。

file: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");

  }
}

Shell命令:(编译并运行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

答案 7 :(得分:6)

String.format()不仅仅是连接字符串。例如,您可以使用String.format()显示特定区域设置中的数字。

但是,如果您不关心本地化,则没有功能差异。 也许一个比另一个快,但在大多数情况下它可以忽略不计......

答案 8 :(得分:4)

通常,字符串连接应优先于String.format。后者有两个主要缺点:

  1. 它不会对以本地方式构建的字符串进行编码。
  2. 构建过程以字符串形式编码。
  3. 从第1点开始,我的意思是无法理解String.format()调用在单个顺序传递中的作用。一个人被迫在格式字符串和参数之间来回,同时计算参数的位置。对于简短的连接,这不是一个问题。但是,在这些情况下,字符串连接不那么详细。

    第2点,我的意思是构建过程的重要部分是用格式字符串(使用DSL)编码的。使用字符串来表示代码有许多缺点。它本身并不是类型安全的,并且使语法高亮,代码分析,优化等变得复杂。

    当然,当使用Java语言外部的工具或框架时,新的因素可以发挥作用。

答案 9 :(得分:2)

我没有做过任何特定的基准测试,但我认为连接可能会更快。 String.format()创建一个新的Formatter,然后创建一个新的StringBuilder(大小只有16个字符)。这是一个相当大的开销,特别是如果你要格式化一个更长的字符串并且StringBuilder不断调整大小。

但是,连接不太有用且难以阅读。与往常一样,值得对代码进行基准测试,看看哪个更好。在资源包,语言环境等加载到内存中并且代码被JIT打印后,服务器应用程序中的差异可以忽略不计。

也许作为一种最佳实践,使用正确大小的StringBuilder(可附加)和Locale创建自己的Formatter是个好主意,如果你有很多格式要做,那就使用它。

答案 10 :(得分:2)

可能存在明显的差异。

String.format非常复杂并且在下面使用正则表达式,所以不要习惯在任何地方使用它,而只是在你需要它的地方。

StringBuilder会快一个数量级(正如这里已有人指出的那样)。

答案 11 :(得分:0)

您无法通过上述程序比较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") ;
}

您会惊讶地发现Format在这里工作得更快。这是因为创建的初始对象可能不会被释放,并且可能存在内存分配问题,从而导致性能问题。

答案 12 :(得分:0)

习惯一点时间来适应String.Format,但是在大多数情况下还是值得的。在NRA(永远不要重复任何内容)的世界中,将标记化消息(日志或用户)保存在Constant库中(我更喜欢相当于静态类的消息)并根据需要使用String.Format调用它们非常有用,无论您是否是否本地化。尝试将这种库与串联方法一起使用,很难通过任何需要串联的方法来读取,排除故障,校对和管理。更换是一种选择,但我怀疑它的性能。经过多年的使用,我遇到的最大问题是String.Format,当我将其传递给另一个函数(如Msg)时,调用的长度很长,但是使用自定义函数作为别名很容易解决

答案 13 :(得分:0)

我认为我们可以使用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样式的格式字符串是在运行时解释的,而不是由编译器验证的,因此它们可能包含导致创建错误字符串的错误。当调用java.util.Formatter,java.lang.String,java.io.PrintStream,MessageFormat和java.io的format(...)方法时,此规则静态验证printf样式格式字符串与其参数的相关性。 .PrintWriter类和java.io.PrintStream或java.io.PrintWriter类的printf(...)方法。

我用花括号替换了printf样式,并得到了一些有趣的结果,如下所示。

  

串联= 69毫秒
格式= 1107毫秒
  格式:弯括号= 416毫秒
MessageFormat = 215   毫秒
MessageFormat:卷曲括号= 2517毫秒

我的结论:
正如我在上面强调的那样,将String.format与花括号一起使用应该是一个不错的选择,以获得良好的可读性和性能。