Java常用类之String、StringBuilder、StringBuffer
对比String,StringBuffer,StringBuilder三者的效率
前提在单线程,同步执行,先执行StringBuffer.append,再执行StringBuilder.append场景,各循环2000次添加字符下
执行java环境jdk1.8
代码:
public class CompareTest {
public static void main(String[] args) {
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuffer的执行时间:{}", (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuilder的执行时间:{}", (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text += i;
}
endTime = System.currentTimeMillis();
log.info("String的执行时间:{}", (endTime - startTime));
}
}
执行结果
10:32:19.431 [main] INFO my.CompareTest - StringBuffer的执行时间:6
10:32:19.437 [main] INFO my.CompareTest - StringBuilder的执行时间:3
10:32:20.391 [main] INFO my.CompareTest - String的执行时间:954
结论:
三者在单线程顺序执行的情况下,三者执行速度 String < StringBuffer <(?存在疑问) StringBuilder
思考由于锁的优化,在性能上StringBuffer 与 StringBuilder对比,在单线程上运行其实是效率差不多的。
但是为什么执行结果会是StringBuffer慢,我尝试了先执行StringBuilder的测试代码
public static void main(String[] args) {
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuilder的执行时间:{}", (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuffer的执行时间:{}", (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text += i;
}
endTime = System.currentTimeMillis();
log.info("String的执行时间:{}", (endTime - startTime));
}
多次运行得到结果
10:42:44.781 [main] INFO my.CompareTest - StringBuilder的执行时间:3
10:42:44.787 [main] INFO my.CompareTest - StringBuffer的执行时间:2
10:42:45.775 [main] INFO my.CompareTest - String的执行时间:988
10:43:14.452 [main] INFO my.CompareTest - StringBuilder的执行时间:5
10:43:14.458 [main] INFO my.CompareTest - StringBuffer的执行时间:2
10:43:15.426 [main] INFO my.CompareTest - String的执行时间:967
10:43:25.328 [main] INFO my.CompareTest - StringBuilder的执行时间:4
10:43:25.335 [main] INFO my.CompareTest - StringBuffer的执行时间:2
10:43:26.295 [main] INFO my.CompareTest - String的执行时间:960
然后我又将String的循环添加放在最上面执行:
public static void main(String[] args) {
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text += i;
}
endTime = System.currentTimeMillis();
log.info("String的执行时间:{}", (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuilder的执行时间:{}", (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuffer的执行时间:{}", (endTime - startTime));
}
这次多次执行得到的结果为:
10:44:30.050 [main] INFO my.CompareTest - String的执行时间:970
10:44:30.055 [main] INFO my.CompareTest - StringBuilder的执行时间:2
10:44:30.057 [main] INFO my.CompareTest - StringBuffer的执行时间:2
10:45:26.505 [main] INFO my.CompareTest - String的执行时间:1026
10:45:26.515 [main] INFO my.CompareTest - StringBuilder的执行时间:2
10:45:26.518 [main] INFO my.CompareTest - StringBuffer的执行时间:3
10:45:38.223 [main] INFO my.CompareTest - String的执行时间:988
10:45:38.228 [main] INFO my.CompareTest - StringBuilder的执行时间:2
10:45:38.231 [main] INFO my.CompareTest - StringBuffer的执行时间:3
10:45:52.584 [main] INFO my.CompareTest - String的执行时间:1020
10:45:52.590 [main] INFO my.CompareTest - StringBuilder的执行时间:2
10:45:52.592 [main] INFO my.CompareTest - StringBuffer的执行时间:2
思考下,其实这样测试不太严谨,因为刚开始运行的时候,字符串常量池中是没有对象的。首次循环的时候会将需要添加的"1",“2”,“3”…字符串常量对象在字符串常量池中创建出来。而后续的循环操作是不需要花费这样的时间的。所以造成了最开始的结论看似是StringBuffer比StringBuilder慢。
然后我单独对StringBuffer和StringBuilder进行20000次添加(String就不测试了,因为有结论肯定比两者都慢):
1.先执行测试代码:
public static void builderTest(){
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuilder的执行时间:{}", (endTime - startTime));
}
执行结果:
StringBuilder的执行时间:3
StringBuilder的执行时间:4
StringBuilder的执行时间:5
2.执行
public static void bufferTest(){
//初始设置
long startTime = 0L;
long endTime = 0L;
StringBuffer buffer = new StringBuffer("");
//开始对比
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
log.info("StringBuffer的执行时间:{}", (endTime - startTime));
}
执行结果:
10:49:24.713 [main] INFO my.CompareTest - StringBuffer的执行时间:3
10:51:33.559 [main] INFO my.CompareTest - StringBuffer的执行时间:4
10:51:33.559 [main] INFO my.CompareTest - StringBuffer的执行时间:5
从结果上看在jdk1.8,单线程的情况下,StringBuffer和StringBuilder循环20000次并没有太大区别。
因为在jdk1.6及之后对synchronized进行了优化,在没有线程竞争的情况下,只会上偏向锁,甚至还会有可能被jvm的逃逸分析分析成未逃逸,优化成无锁状态,所以二者并没有差别。