如果性能很重要,我应该使用Java的String.format()吗?

我们必须一直为日志输出构建字符串,依此类推。在JDK版本中,我们已经学会了何时使用(许多追加,线程安全)和(许多追加,非线程安全)。StringBufferStringBuilder

使用的建议是什么?它是否有效,或者我们是否被迫坚持对性能很重要的单行串联?String.format()

例如丑陋的旧风格,

String s = "What do you get if you multiply " + varSix + " by " + varNine + "?";

与整洁的新样式(String.format,可能更慢),

String s = String.format("What do you get if you multiply %d by %d?", varSix, varNine);

注意:我的具体用例是整个代码中的数百个“单行”日志字符串。它们不涉及循环,因此重量级太重。我对此特别感兴趣。StringBuilderString.format()


答案 1

我取了hhafez代码并添加了一个内存测试

private static void test() {
    Runtime runtime = Runtime.getRuntime();
    long memory;
    ...
    memory = runtime.freeMemory();
    // for loop code
    memory = memory-runtime.freeMemory();

我为每个方法分别运行它,'+'运算符,String.format和StringBuilder(调用string()),因此使用的内存不会受到其他方法的影响。我添加了更多的串联,使字符串成为“Blah”+ i + “Blah”+ i +“Blah” + i + “Blah”。

结果如下(平均每次运行 5 次):
接近时间(毫秒) 分配的内存(长)
'+' 运算符 747 320,504
String.format 16484 373,312
StringBuilder 769 57,344

我们可以看到 String '+' 和 StringBuilder 在时间上几乎是相同的,但 StringBuilder 在内存使用方面要高效得多。当我们在足够短的时间间隔内有许多日志调用(或任何其他涉及字符串的语句)时,这一点非常重要,因此垃圾回收器将无法清理由'+'运算符产生的许多字符串实例。

顺便说一句,在构造消息之前,不要忘记检查日志记录级别

结论:

  1. 我将继续使用StringBuilder。
  2. 我有太多的时间或太少的生命。

答案 2

我写了一个小类来测试,它具有两者更好的性能,并且+排在格式之前。乘以5至6倍。试试你自己

import java.io.*;
import java.util.Date;

public class StringTest{

    public static void main( String[] args ){
    int i = 0;
    long prev_time = System.currentTimeMillis();
    long time;

    for( i = 0; i< 100000; 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<100000; i++){
        String s = String.format("Blah %d Blah", i);
    }
    time = System.currentTimeMillis() - prev_time;
    System.out.println("Time after for loop " + time);

    }
}

对不同的N运行上述操作表明,两者的行为都是线性的,但速度要慢5-30倍。String.format

原因是在当前实现中,首先使用正则表达式解析输入,然后填充参数。另一方面,与plus的串联由javac(而不是JIT)优化并直接使用。String.formatStringBuilder.append

Runtime comparison