1、String
String是不可变的字符串,比如字符串string="abc",在是如果想改变字符串的值,string = "def",JVM会生成新的字符串"def",而不是在原来的字符串上改,原来的字符串"abc"会被垃圾回收器收回。
2、StingBuffer
StringBuffer是可变的字符串,如果修改StringBuffer的值,是直接在原来的字符串上修改。
StringBuffer是线程安全的。
3、StringBuilder
StringBuffer是可变的字符串,如果修改StringBuffer的值,是直接在原来的字符串上修改。
StringBuffer不是线程安全的。
4、三者比对
在字符串拼接的过程中,三者的效率最高是StringBuilder,其次是StringBuffer,最后是String。
我们来看一下程序实际运行的情况
public class StringTest {
public static void main(String[] args) {
String value = "1234567890";
int count = 30000;
System.out.println("String StringBuffer StringBuilder");
for (int j = 1; j <= 10; j++) {
String string = "";
StringBuffer stringBuffer = new StringBuffer();
StringBuilder stringBuilder = new StringBuilder();
long begin = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
string = string + value;
}
long end1 = System.currentTimeMillis();
System.out.print((end1 - begin) + "毫秒 ");
for (int i = 0; i < count; i++) {
stringBuffer.append(value);
}
long end2 = System.currentTimeMillis();
System.out.print((end2 - end1) + "毫秒 ");
for (int i = 0; i < count; i++) {
stringBuilder.append(value);
}
long end3 = System.currentTimeMillis();
System.out.println((end3 - end2) + "毫秒");
}
}
}
运行结果是:
String StringBuffer StringBuilder
8187毫秒 0毫秒 0毫秒
7906毫秒 0毫秒 16毫秒
7859毫秒 0毫秒 16毫秒
7921毫秒 0毫秒 0毫秒
7813毫秒 16毫秒 0毫秒
7890毫秒 0毫秒 0毫秒
7985毫秒 15毫秒 0毫秒
8438毫秒 0毫秒 0毫秒
8172毫秒 0毫秒 0毫秒
7937毫秒 0毫秒 16毫秒
上面字符串的拼接次数是30000,String的执行时间大概是8秒,由于StringBuffer和StringBuilder的执行速度比String要快很多,所以上面这两个字符串的执行时间几乎为零。
下面增大拼接次数到3000000,直接对比StringBuffer和StringBuilder的执行时间。
public class StringTest {
public static void main(String[] args) {
String value = "1234567890";
int count = 3000000;
System.out.println("StringBuffer StringBuilder");
for (int j = 1; j <= 10; j++) {
StringBuffer stringBuffer = new StringBuffer();
StringBuilder stringBuilder = new StringBuilder();
long begin = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
stringBuffer.append(value);
}
long end1 = System.currentTimeMillis();
System.out.print((end1 - begin) + "毫秒 ");
for (int i = 0; i < count; i++) {
stringBuilder.append(value);
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end1) + "毫秒");
}
}
}
程序的运行结果是
StringBuffer StringBuilder
281毫秒 219毫秒
234毫秒 188毫秒
234毫秒 219毫秒
219毫秒 187毫秒
234毫秒 204毫秒
250毫秒 156毫秒
281毫秒 234毫秒
266毫秒 234毫秒
219毫秒 188毫秒
234毫秒 203毫秒
说明StringBuilder的执行效率比StringBuffer高一些,主要原因是StringBuffer要考虑线程安全。
public final class StringBuffer{
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
}
而StringBuilder则不需要考虑线程安全。
public final class StringBuilder{
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
5、StringBuffer、StringBuilder指定初始容量
StringBuffer和StringBuilder的初始容量都是16,看以下源代码
public final class StringBuffer extends AbstractStringBuilder{
public StringBuffer() {
super(16);
}
}
abstract class AbstractStringBuilder{
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}
在字符串拼音的过程中,如果拼接后的长度超过原来的容量,StringBuffer会自动将容量扩展到原来的2倍左右,看以下源代码
public final class StringBuffer extends AbstractStringBuilder{
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
}
abstract class AbstractStringBuilder{
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
}
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}
value = Arrays.copyOf(value, newCapacity);
}
}
因此,如果事先知道字符串拼接后的容量,事先就指定StringBuffer或StringBuilder的容量,那么执行速度就会快很多。
下面用程序比对一下两者的执行情况。
public static void specifyStringBufferCapacity(){
String value = "1234567890";
int count = 3000000;
System.out.println("StringBuffer StringBuffer(specify Capacity)");
for (int j = 1; j <= 10; j++) {
StringBuffer stringBuffer1 = new StringBuffer();
StringBuffer stringBuffer2 = new StringBuffer(count*value.length());
long begin = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
stringBuffer1.append(value);
}
long end1 = System.currentTimeMillis();
System.out.print((end1 - begin) + "毫秒 ");
for (int i = 0; i < count; i++) {
stringBuffer2.append(value);
}
long end2 = System.currentTimeMillis();
System.out.println((end2 - end1) + "毫秒");
}
}
执行结果如下:
StringBuffer StringBuffer(specify Capacity)
313毫秒 141毫秒
219毫秒 125毫秒
218毫秒 125毫秒
219毫秒 125毫秒
219毫秒 141毫秒
203毫秒 141毫秒
219毫秒 125毫秒
219毫秒 125毫秒
234毫秒 141毫秒
219毫秒 140毫秒
从执行结果中可以看出,指定初始容量的执行时间比没指定初始容量的执行时间要快80%以上。
5、总结
三者的使用场景如下:
(1)String :字符串不需要经常改变
(2)StringBuffer:字符串经常需要改变且有线程安全问题
(3)StringBuilder:字符串经常需要改变且不考虑线程安全问题