在第一次拼接的时候(连续加号),先创建了一个 StringBuilder 对象,然后 append 当前的字符串对象 aa,接着连续 append 将要拼接的元素,最后 toString() 返回拼接后的字符串对象赋给 aa;
第二次拼接,同样是创建一个 StringBuilder 对象,然后 append 当前的字符串对象 aa,接着 append 8,最后 toString() 返回拼接后的字符串对象赋给 aa;
第三次拼接,同样是创建一个 StringBuilder 对象,然后 append 当前的字符串对象 aa,接着 append "tt",最后 toString() 返回拼接后的字符串对象赋给 bb;
我们把每出现一次 “=” 算成一次拼接,那么每次拼接都会创建一个 StringBuilder 对象。
当遇到大规模的场景中,比如循环次数很多,就像下面的例子:
public class Test1 { public static void main(String[] args) { Test1 test = new Test1(); System.out.println(test.testString()); System.out.println(test.testStringBuilder()); } public long testString(){ String a = ""; long start = Calendar.getInstance().getTimeInMillis(); for(int i=0;i<100000;i++){ a += i; } long end = Calendar.getInstance().getTimeInMillis(); return end-start; } public long testStringBuilder(){ StringBuilder a = new StringBuilder(); long start = Calendar.getInstance().getTimeInMillis(); for(int i=0;i<100000;i++){ a.append(i); } long end = Calendar.getInstance().getTimeInMillis(); return end-start; } }
输出:
22243 16
耗时比较,前者呈指数级增长,而后者是线性增长。性能上相差甚远。
甚至如果我们已经知道了容量,还可以继续优化,一次性分配一个 StringBuilder,避免扩容时候的开销。参考下面例子。
public class Test2 { public static void main(String[] args) { Test2 test = new Test2(); for(int i=0;i<5;i++){ System.out.println(test.testStringBuilder() + "---" + test.testStringBuilder2()); } } public long testStringBuilder(){ StringBuilder a = new StringBuilder(); long start = Calendar.getInstance().getTimeInMillis(); for(int i=0;i<10000000;i++){ a.append(1); } long end = Calendar.getInstance().getTimeInMillis(); return end-start; } public long testStringBuilder2(){ StringBuilder a = new StringBuilder(10000000); long start = Calendar.getInstance().getTimeInMillis(); for(int i=0;i<10000000;i++){ a.append(1); } long end = Calendar.getInstance().getTimeInMillis(); return end-start; } }
输出:
78---16 62---31 47---15 63---31 47---31
提前分配,耗时更短~
原则很简单:不要使用字符串连接操作符来合并多个字符串,除非性能无关紧要。应该使用 StringBuilder 的 append 方法。