使用JMH(Java Microbenchmark Harness)測試Java字符串拼接性能

最近因爲項目須要,須要比較各類狀況下進行字符串拼接的性能。主要的字符串拼接方法有下面四種:java

  1. 字符串加法: 「Hello」 + "Word";
  2. StringBuilder:  new StringBuilder("Hello").append("World");
  3. 調用String.format模板方法: String.format("%s%s","Hello","World");
  4. 筆者本身編寫的slf4j風格的PlaceholderFormat: FastStringUtils.placeholderFomat("{}{}","Hello","World")

JMH是openJDK JIT小組研發的微基準測試框架,結果仍是比較可靠的。app

可是,在編寫測試用例的時候,有幾點須要注意:框架

  • 不要在測試方法內部建立循環,框架自己會屢次調用;
  • 不要對常量進行字符串加減操做來測試性能,JIT很是聰明,可能會優化成運行時常量,測試毫無心義;
  • 儘可能減小沒必要要的操做,好比動態生成字符串,要提早建立好

如下是測試用例:dom

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3)
@Measurement(iterations = 7)
public class StringFormatTest {

    private static final int SIZE = 0x4ff;

    private int index = 0;

    private final String[] a = new String[SIZE];
    private final String[] b = new String[SIZE];
    private final String[] c = new String[SIZE];

    @Setup
    public void setup() {
        Random random = new Random();
        for (int i = 0; i < SIZE; i++) {
            a[i] = random.nextInt() + "";
            b[i] = random.nextLong() + "";
            c[i] = random.nextDouble() + "";
        }
    }

    @Benchmark
    public String testStringFormat() {
        if (++index >= SIZE)
            index = 0;
        return String.format("%s:%s:%s", a[index], b[index], c[index]);
    }

    @Benchmark
    public String testStringAdd() {
        if (++index >= SIZE)
            index = 0;
        return a[index] + ':' + b[index] + ':' + c[index];
    }

    @Benchmark
    public String testStringBuilder() {
        if (++index >= SIZE)
            index = 0;
        return new StringBuilder(a[index])
                .append(':')
                .append(b[index])
                .append(':')
                .append(c[index])
                .toString();
    }

    @Benchmark
    public String testPlaceholderFormat() {
        if (++index >= SIZE)
            index = 0;
        return FastStringUtils.placeholderFormat("{}:{}:{}", a[index], b[index], c[index]);
    }

}

測試結果以下:性能

Benchmark                               Mode  Cnt     Score    Error  Units
StringFormatTest.testPlaceholderFormat  avgt   70   130.732 ±  2.519  ns/op
StringFormatTest.testStringAdd          avgt   70   102.576 ±  1.368  ns/op
StringFormatTest.testStringBuilder      avgt   70    94.908 ±  1.097  ns/op
StringFormatTest.testStringFormat       avgt   70  1156.455 ± 16.516  ns/op

總結一下:測試

  • String.format雖然有着更好的可讀性,但性能遠遠不及其它方式,在性能敏感的場景應該謹慎使用;
  • StringBuilder是最快的;
  • String加法和StringBuilder性能差異不大,具體可能與編譯器的優化有關係;
  • placeholderFomat性能不差,且有着不錯的可讀性

另,貼上placeholderFomat源碼:優化

public static String placeholderFormat(String s, Object... args) {
        int len1 = s.length(), len2 = args.length;
        StringBuilder builder = new StringBuilder();
        int i = 0, j = 0;
        while (i < len1) {
            char c = s.charAt(i);
            if (c == '{') {
                if (s.charAt(i + 1) == '}' && j < len2) {
                    builder.append(args[j++]);
                    i += 2;
                    continue;
                } else {
                    builder.append('{');
                }
            } else {
                builder.append(c);
            }
            i++;
        }
        return builder.toString();

    }
相關文章
相關標籤/搜索