这里我们中一个相同的例子来对比这三种连接字符串所用的时间,如下:
long t1 = System.currentTimeMillis();
//StringBuilder sb = new StringBuilder();
//StringBuffer sb = new StringBuffer();
String s = "";
for(int i = 0;i < 100; i++) {
s = s+i;
//sb.append(i);
}
long t2 = System.currentTimeMillis();
System.out.println(t2-t1);
MAC 系统,java8, 16G内存
i == 100
+ | StringBuilder | StringBuffer |
---|---|---|
0 | 0 | 0 |
i == 1000
+ | StringBuilder | StringBuffer |
---|---|---|
10 | 0 | 0 |
i == 10000
+ | StringBuilder | StringBuffer |
---|---|---|
420 | 3 | 3 |
i == 100000
+ | StringBuilder | StringBuffer |
---|---|---|
24656 | 15 | 19 |
i == 1000000
+ | StringBuilder | StringBuffer |
---|---|---|
…. | 62 | 86 |
经过从100-1000000的测试得出下面的结论:
100次的连接三者所消耗的时间几乎差不多
1000次的连接+号性能没有StringBuffer 和StringBuilder性能好,后两个速度也不相上下
100000次连接+号时间大大增加,这时的StringBuilder所花的时间略少于StringBuffer
1000000次连接+号已经崩溃了,StringBuilder所花的时间比StringBuffer少4分之一
为什么加号在1000次连接以后速度明显变长了呢?
String首先是不可变的,每次连接都需要创建个新的String对象,所以随着连接次数的增加不停的创建的对象甚至可能还会导致内存溢出。
StringBuffer和StringBuilder则采用了“缓存”的方式,预先设置容量,减少生成对象次数,如下代码:
/**
* Constructs a string buffer with no characters in it and an
* initial capacity of 16 characters.
*/
public StringBuffer() {
super(16);
}
还可以设置capacity的大小:
/**
* Constructs a string buffer with no characters in it and
* the specified initial capacity.
*
* @param capacity the initial capacity.
* @exception NegativeArraySizeException if the {@code capacity}
* argument is less than {@code 0}.
*/
public StringBuffer(int capacity) {
super(capacity);
}
为什么在十万次以上连接的时候StringBuilder所用的时间要少于StringBuffer呢?
他们都继承自AbstractStringBuilder,方法也都差不多,所不同的是StringBuffer用了锁(synchronized),所以性能上有所下降
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
所以StringBuffer是多线程安全的,而StringBuilder是线程非安全的,在使用上要有所区分