我们必须一直为日志输出构建字符串等等。在JDK版本中,我们已经学习了什么时候使用StringBuffer(许多追加,线程安全)和StringBuilder(许多追加,非线程安全)。
使用String.format()有什么建议?它是有效的,还是我们被迫坚持在性能很重要的一行程序中使用连接?
例如,丑陋的老式风格,
String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";
vs.整洁的新样式(字符串。格式,可能更慢),
String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);
注意:我的特定用例是代码中的数百个“一行”日志字符串。它们不涉及循环,所以StringBuilder太重量级了。我对String.format()特别感兴趣。
下面是修改后的hhafez入口。它包括一个字符串构建器选项。
public class BLA
{
public static final String BLAH = "Blah ";
public static final String BLAH2 = " Blah";
public static final String BLAH3 = "Blah %d Blah";
public static void main(String[] args) {
int i = 0;
long prev_time = System.currentTimeMillis();
long time;
int numLoops = 1000000;
for( i = 0; i< numLoops; i++){
String s = BLAH + i + BLAH2;
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for( i = 0; i<numLoops; i++){
String s = String.format(BLAH3, i);
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for( i = 0; i<numLoops; i++){
StringBuilder sb = new StringBuilder();
sb.append(BLAH);
sb.append(i);
sb.append(BLAH2);
String s = sb.toString();
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
}
}
循环391之后的时间
循环4163之后的时间
循环227之后的时间
Java的字符串。格式如下:
它解析格式字符串,分解成一个格式块列表
它迭代格式块,呈现为一个StringBuilder,这基本上是一个数组,根据需要调整自己的大小,通过复制到一个新的数组。这是必要的,因为我们还不知道分配最终字符串的大小
StringBuilder.toString()将他的内部缓冲区复制到一个新的String中
如果这个数据的最终目的地是一个流(例如,渲染一个网页或写入一个文件),你可以将格式块直接组装到你的流中:
new PrintStream(outputStream, autoFlush, encoding).format("hello {0}", "world");
我推测优化器将优化掉格式字符串处理。如果是这样,就剩下了与手动展开String相同的平摊性能。格式转换为StringBuilder。
我只是修改了hhafez的测试,以包括StringBuilder。StringBuilder比String快33倍。格式使用jdk 1.6.0_10客户端XP。使用-server开关将该因子降低到20。
public class StringTest {
public static void main( String[] args ) {
test();
test();
}
private static void test() {
int i = 0;
long prev_time = System.currentTimeMillis();
long time;
for ( i = 0; i < 1000000; i++ ) {
String s = "Blah" + i + "Blah";
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for ( i = 0; i < 1000000; i++ ) {
String s = String.format("Blah %d Blah", i);
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
prev_time = System.currentTimeMillis();
for ( i = 0; i < 1000000; i++ ) {
new StringBuilder("Blah").append(i).append("Blah");
}
time = System.currentTimeMillis() - prev_time;
System.out.println("Time after for loop " + time);
}
}
虽然这听起来可能很激烈,但我认为它只在极少数情况下相关,因为绝对数字非常低:100万个简单字符串对应4个s。格式调用是可以的-只要我使用他们的日志或类似的。
更新:正如sjbotha在评论中指出的,StringBuilder测试是无效的,因为它缺少最后的. tostring()。
在我的机器上,从String.format(.)到StringBuilder的正确加速因子是23(使用-server开关时为16)。