Java 中的 StringBuilder 与 StringBuffer
1. 概述
在这篇简短的文章中,我们将了解Java 中的 StringBuilder 和 StringBuffer。
简而言之,StringBuilder是在Java 1.5中引入的,作为StringBuffer的替代品。
2. 相似之处
StringBuilder 和 StringBuffer 都会创建保存可变字符序列的对象。让我们看看它是如何工作的,以及它与不可变的 String 类相比有何不同:
String immutable = "abc";
immutable = immutable + "def";
尽管看起来我们是通过附加“def”来修改同一个对象,但我们实际上是在创建一个新对象,因为 String 实例无法修改。
当使用StringBuffer或StringBuilder时,我们可以使用append()方法:
StringBuffer sb = new StringBuffer("abc");
sb.append("def");
在这种情况下,没有创建新对象。我们在 sb 实例上调用了 append() 方法并修改了其内容。 StringBuffer 和 StringBuilder 是可变对象。
3. 差异
StringBuffer 是同步的,因此是线程安全的。 StringBuilder 与 StringBuffer API 兼容,但不保证同步。
因为它不是线程安全的实现,所以速度更快,建议在不需要线程安全的地方使用它。
3.1.性能
在小迭代中,性能差异微不足道。让我们使用 JMH 进行快速微基准测试:
@State(Scope.Benchmark)
public static class MyState {
int iterations = 1000;
String initial = "abc";
String suffix = "def";
}
@Benchmark
public StringBuffer benchmarkStringBuffer(MyState state) {
StringBuffer stringBuffer = new StringBuffer(state.initial);
for (int i = 0; i < state.iterations; i++) {
stringBuffer.append(state.suffix);
}
return stringBuffer;
}
@Benchmark
public StringBuilder benchmarkStringBuilder(MyState state) {
StringBuilder stringBuilder = new StringBuilder(state.initial);
for (int i = 0; i < state.iterations; i++) {
stringBuilder.append(state.suffix);
}
return stringBuilder;
}
我们使用默认的吞吐量模式 - 即每单位时间的操作(分数越高越好),它给出:
Benchmark Mode Cnt Score Error Units
StringBufferStringBuilder.benchmarkStringBuffer thrpt 200 86169.834 ± 972.477 ops/s
StringBufferStringBuilder.benchmarkStringBuilder thrpt 200 91076.952 ± 2818.028 ops/s
如果我们将迭代次数从 1k 增加到 1m,那么我们会得到:
Benchmark Mode Cnt Score Error Units
StringBufferStringBuilder.benchmarkStringBuffer thrpt 200 77.178 ± 0.898 ops/s
StringBufferStringBuilder.benchmarkStringBuilder thrpt 200 85.769 ± 1.966 ops/s
但是,请记住,这是一个微基准,它可能会也可能不会对应用程序的实际性能产生真正的影响。
4. 结论
简而言之,StringBuffer是一个线程安全的实现,因此比StringBuilder慢。
在单线程程序中,我们可以使用StringBuilder。然而,StringBuilder 相对于 StringBuffer 的性能提升可能太小,不足以证明在各处替换它是合理的。分析应用程序和在进行任何将一种实现替换为另一种实现的工作之前,请先了解其运行时性能特征。
最后,一如既往,讨论期间使用的代码可以在 GitHub 上找到。