String.format
在Java中,使用和字符串串联之间有明显的区别吗?
我倾向于使用,String.format
但偶尔会滑动并使用串联。我想知道一个是否比另一个更好。
我的观察方式String.format
为您提供了“格式化”字符串的更多功能;并置意味着您不必担心意外地放入额外的%s或丢失其中一个。
String.format
也较短。
哪个更易读取决于您的头部如何工作。
String.format
在Java中,使用和字符串串联之间有明显的区别吗?
我倾向于使用,String.format
但偶尔会滑动并使用串联。我想知道一个是否比另一个更好。
我的观察方式String.format
为您提供了“格式化”字符串的更多功能;并置意味着您不必担心意外地放入额外的%s或丢失其中一个。
String.format
也较短。
哪个更易读取决于您的头部如何工作。
Answers:
我建议使用是一种更好的做法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");
}
计时结果如下:
因此,连接比String.format快得多。
javap -c StringTest.class
,则仅当您不在循环中时,编译器才会自动将“ +”转换为StringBuilder 。如果连接全部在一行上完成,则与使用'+'相同,但是如果使用myString += "morechars";
或myString += anotherString;
在多行上进行连接,您会注意到可能会创建多个StringBuilder,因此使用“ +”并不总是那么有效作为StringBuilder。
+
不会转换为StringBuilder.append()
,而是new StringBuilder()
在每次迭代中发生。
既然有关于性能的讨论,我想我会添加一个包含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");
String
。公平地说,StringBuilder测试需要最后一步,将StringBuilder的内容转换为String。您可以通过致电来做到这一点bldString.toString()
。我希望能解释吗?
String s = bldString.toString();
定时用级联和StringBuilder的几乎看齐,与对方: Format = 1520 millisecond
,Concatenation = 167 millisecond
,String Builder = 173 millisecond
我跑他们在一个循环中,平均每个出去找一个好名声:(预JVM的优化,将尝试10000+循环时,我得到的时间)
问题之一.format
是您失去了静态类型安全性。您的格式参数可能太少,格式说明符的类型也可能错误-两者都导致一个IllegalFormatException
at runtime,因此最终可能会导致记录代码破坏生产。
相反,to的参数+
可以由编译器测试。
哪个更易读取决于您的头部如何工作。
您在那里得到了答案。
这是个人喜好问题。
我想,字符串连接的速度稍快一些,但这应该可以忽略不计。
这是一个以毫秒为单位的多个样本大小的测试。
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
这是与上述相同的测试,只是修改了对StringBuilder的toString()方法的调用。下面的结果表明,StringBuilder方法比使用+运算符的String连接要慢一些。
文件: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
通常,应优先使用字符串连接String.format
。后者有两个主要缺点:
到第1点,我的意思是不可能String.format()
一次连续地了解一次呼叫在做什么。一个人被迫在格式字符串和参数之间来回移动,同时计算参数的位置。对于短串联,这不是什么大问题。但是,在这些情况下,字符串连接不太冗长。
在第2点,我的意思是构建过程的重要部分以格式字符串(使用DSL)进行编码。使用字符串表示代码有很多缺点。它本身不是类型安全的,并且使语法突出显示,代码分析,优化等复杂化。
当然,当使用Java语言外部的工具或框架时,新因素会起作用。
我没有做任何特定的基准测试,但是我认为串联可能会更快。String.format()创建一个新的Formatter,后者又创建一个新的StringBuilder(大小仅为16个字符)。这是相当大的开销,尤其是当您格式化较长的字符串并且StringBuilder不得不调整大小时。
但是,级联的作用较小,更难阅读。与往常一样,值得对您的代码进行基准测试,看看哪个更好。在将资源包,语言环境等加载到内存中并且对代码进行JITted处理后,在服务器应用程序中的差异可以忽略不计。
也许作为最佳实践,最好使用大小合适的StringBuilder(附加)和Locale创建自己的Formatter,并在需要进行大量格式化的情况下使用它。
我认为我们可以接受,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-style
格式字符串是在运行时解释的,而不是由编译器验证的,所以它们可能包含导致创建错误字符串的错误。这条规则静态验证的相关printf-style
调用的格式(...)方法时,格式化字符串到他们的论点java.util.Formatter
,java.lang.String
,java.io.PrintStream
,MessageFormat
,和java.io.PrintWriter
类和printf(...)
方法java.io.PrintStream
或java.io.PrintWriter
类。
我用花括号替换了printf样式,并得到了一些有趣的结果,如下所示。
串联= 69毫秒
格式= 1107毫秒
格式:curly-brackets = 416毫秒
MessageFormat = 215毫秒
MessageFormat:curly-brackets = 2517毫秒
我的结论:
正如我在上面强调的那样,将String.format与花括号一起使用应该是一个不错的选择,以获得良好的可读性和性能。
您无法通过上述程序比较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在这里工作得更快。这是因为创建的初始对象可能不会释放,并且内存分配和性能可能存在问题。