String vs StringBuilder vs StringBuffer in Java

#String vs StringBuilder vs StringBuffer

概述

String类使用final关键字字符数组来保存字符串,private final char value[],所以String对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,而AbstractStringBuilder源码:

1
2
3
4
5
6
7
8
9
10
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}

AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}

性能

在字符串最常用的操作中,比如字符串的拼接。StringBuffer和StringBuilder要远比String类的操作更快。接下来以String和Stringbuffer的比较为例。

String类的拼接操作一般为:

1
2
String str = new String ("Stanford  ");
str += "Lost!!";

我们来看看字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0 new #7 <Class java.lang.String>
3 dup
4 ldc #2 <String "Stanford ">
6 invokespecial #12 <Method java.lang.String(java.lang.String)>
9 astore_1
10 new #8 <Class java.lang.StringBuffer>
13 dup
14 aload_1
15 invokestatic #23 <Method java.lang.String valueOf(java.lang.Object)>
18 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>
21 ldc #1 <String "Lost!!">
23 invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>
26 invokevirtual #22 <Method java.lang.String toString()>
29 astore_1

其中0到9是执行String类的初始化,而后面则是拼接操作的字节码,可以看到生成的字节码中创建了一个StringBuffer对象,并且调用了append方法。最后再调用toString()方法转换回String对象。整个过程的操作比较昂贵。

而如果我们使用StringBuffer进行拼接操作:

1
2
StringBuffer str = new StringBuffer ("Stanford ");
str.append("Lost!!");

至于该操作的字节码为:

1
2
3
4
5
6
7
8
9
0 new #8 <Class java.lang.StringBuffer>
3 dup
4 ldc #2 <String "Stanford ">
6 invokespecial #13 <Method java.lang.StringBuffer(java.lang.String)>
9 astore_1
10 aload_1
11 ldc #1 <String "Lost!!">
13 invokevirtual #15 <Method java.lang.StringBuffer append(java.lang.String)>
16 pop

线程安全性

由于String对象是不可变的,因此常量为线程安全的。而StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

总结

  • 操作少量的数据,使用String;
  • 单线程操作字符串,大量数据,使用StringBuilder;
  • 多线程操作字符串,大量数据,使用StringBuilder;