StringBuilder 提高了多次添加的内存消耗和性能。让我们分析下一个示例(假设 javac 没有优化任何 String 连接):
String s = "a" + "b" + "c" + "d" + ... + "z";
StringBuilder sb = new StringBuilder("a").append("b").append("c")....append("z");
如果String与+串联,java将从左到右添加字符串,每次创建一个新字符串:"ab",然后"abc",然后"abcd",因此有25个新字符串,以及每次它都会完全复制以前的结果。而StringBuilder 将简单地将每个字符串添加到其自己的char[] 数组中,而不会创建任何冗余对象。
现在让n 是字符串的数量,l - 每个字符串的长度。在这种情况下,mth + 的复杂度将是 O(l*m),因为每次都会复制整个先前的字符串连接。因此,我们可以得出结论,对于String 情况,汇总时间(和内存(!))复杂度将为O(l*n*n)。而在StringBuilder 的情况下,它将是O(l*n)。
还有关于日志记录 - 小的性能比较:
@Benchmark
public void stringConcatenation(Blackhole bh) {
// By using all these string we should prevent string builder optimizations.
String a = "start ";
String b = a + "first";
String c = b + " inside ";
String d = c + "second";
String e = d + ", ";
String f = e + 1024;
bh.consume(a);
bh.consume(b);
bh.consume(c);
bh.consume(d);
bh.consume(e);
bh.consume(f);
}
@Benchmark
public void stringBuilder(Blackhole bh) {
StringBuilder sb = new StringBuilder("start ")
.append("first")
.append(" inside ")
.append("second")
.append(", ")
.append(1024);
bh.consume(sb.toString());
}
@Benchmark
public void logback(Blackhole bh) {
// Logback formatting
bh.consume(MessageFormatter.arrayFormat("start {} inside {}, {}", new Object[]{"first", "second", 1024}).getMessage());
}
@Benchmark
public void log4j(Blackhole bh) {
// Log4j formatting
bh.consume(messageFactory.newMessage("start {} inside {}, {}", "first", "second", 1024));
}
结果:
Benchmark Mode Cnt Score Error Units
LogBenchmark.stringConcatenation thrpt 5 9080147,269 ? 988134,269 ops/s
LogBenchmark.stringBuilder thrpt 5 27136050,849 ? 2776464,863 ops/s
LogBenchmark.logback thrpt 5 3579746,331 ? 346554,072 ops/s
LogBenchmark.log4j thrpt 5 4992342,169 ? 335971,537 ops/s
因此,正如您所看到的,如果您实际记录大量日志,那么“改用日志框架格式化程序”可能不是更好的选择。