Difference between StringBuilder and StringBuffer

Difference between StringBuilder and StringBuffer

技术背景

在 Java 编程中,处理字符串是常见的操作。String 类是不可变的,当需要频繁修改字符串内容时,使用 String 会导致性能问题,因为每次修改都会创建一个新的 String 对象。为了解决这个问题,Java 提供了 StringBufferStringBuilder 两个类,它们都是可变的字符串序列。

实现步骤

相似点

StringBuilderStringBuffer 都是可变的,意味着可以在同一位置更改它们的内容。并且它们都有相同签名的方法。

不同点

  • 线程安全性StringBuffer 是同步的,因此是线程安全的;而 StringBuilder 是非同步的,不是线程安全的。
  • 性能:由于 StringBuffer 的同步机制会带来额外的开销,所以 StringBuilder 的性能通常比 StringBuffer 更好。

代码示例

以下是一个简单的基准测试代码,用于比较 StringBufferStringBuilder 的性能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Main {
public static void main(String[] args) {
int N = 77777777;
long t;

{
StringBuffer sb = new StringBuffer();
t = System.currentTimeMillis();
for (int i = N; i > 0 ; i--) {
sb.append("");
}
System.out.println(System.currentTimeMillis() - t);
}

{
StringBuilder sb = new StringBuilder();
t = System.currentTimeMillis();
for (int i = N; i > 0 ; i--) {
sb.append("");
}
System.out.println(System.currentTimeMillis() - t);
}
}
}

运行这个测试,StringBuffer 可能需要 2241 ms,而 StringBuilder 只需要 753 ms。

以下是一个多线程环境下的测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class StringsPerf {

public static void main(String[] args) {

ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//With Buffer
StringBuffer buffer = new StringBuffer();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(buffer));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Buffer : " + AppendableRunnable.time);

//With Builder
AppendableRunnable.time = 0;
executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
StringBuilder builder = new StringBuilder();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(builder));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Builder: " + AppendableRunnable.time);

}

static void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown();
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (Exception e) {}
}
}

class AppendableRunnable<T extends Appendable> implements Runnable {

static long time = 0;
T appendable;
public AppendableRunnable(T appendable){
this.appendable = appendable;
}

@Override
public void run(){
long t0 = System.currentTimeMillis();
for (int j = 0 ; j < 10000 ; j++){
try {
appendable.append("some string");
} catch (Exception e) {}
}
time+=(System.currentTimeMillis() - t0);
}
}

在多线程环境下,StringBuffer 可以正常工作,但 StringBuilder 可能会抛出 java.lang.ArrayIndexOutOfBoundsException 异常。

最佳实践

  • 当需要在单线程环境下频繁修改字符串内容时,建议使用 StringBuilder,因为它的性能更好。
  • 当需要在多线程环境下频繁修改字符串内容时,建议使用 StringBuffer,以确保线程安全。

常见问题

1. StringBuilderStringBuffer 的性能差异在所有情况下都很大吗?

在单线程环境下,StringBuilder 通常比 StringBuffer 快,因为它不需要同步机制带来的额外开销。但在某些小迭代的情况下,由于 JVM 优化(如锁消除),性能差异可能可以忽略不计。

2. StringBuilder 一定不能在多线程环境中使用吗?

StringBuilder 不是线程安全的,但如果能确保在多线程环境中对 StringBuilder 的访问是串行的,或者通过其他方式进行同步,也可以使用。不过,在这种情况下,使用 StringBuffer 会更简单和安全。


Difference between StringBuilder and StringBuffer
https://119291.xyz/posts/difference-between-stringbuilder-and-stringbuffer/
作者
ww
发布于
2025年5月27日
许可协议