为什么String用"+"拼接字符串效率低下,最好能从JVM角度谈谈吗?
对于这个问题,我们先来看看如下代码:
public class StringTest {
public static void main(String[] args) {
String a = "abc";
String b = "def";
String c = a + b;
String d = "abc" + "def";
System.out.Println(c);
System.out.Println(d);
}
}
打印结果:
abcdef
abcdef
从上面代码示例中,我们看到两种方式拼接的字符串打印的结果是一样的。但这只是表面上的,实际内部运行不一样。
两者究竟有什么不一样?
String c = a + b; "+"相当于new了一个StringBuilder,然后调用StringBuilder的初始化方法,然后进行append操作,最后toString();
String d = “abc” + “def”; 两个常量在同行发生时,JVM在编译时就认为这个“+”是没有用处的,编译时直接变成String d = “abcdef”;
那么效率问题从何说起?
其实上面两个例子,连接字符串行表达式很简单,那么"+"和StringBuilder基本是一样的,但如果结构比较复杂,如使用循环来连接字符串,那么产生的Java Byte Code就会有很大的区别。我们再来看看下面一段代码:
import java.util.*;
public class StringTest {
public static void main(String[] args){
String s = "";
Random rand = new Random();
for (int i = 0; i < 10; i++){
s = s + rand.nextInt(1000) + " ";
}
System.out.println(s);
}
}
由上可知,“+”在for循环内部,所以每次执行就会创建一个StringBuilder,虽然Java有垃圾回收器,但是回收器的工作时间不固定,如果不断产生垃圾,会占用大量的资源。所以应该在程序中直接使用StringBuilder来连接字符串,如下:
import java.util.Random;
public class StringTest {
public static void main(String[] args) {
Random rand = new Random();
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10; i++) {
result.append(rand.nextInt(1000));
result.append(" ");
}
System.out.println(result.toString());
}
}
从上面的反编译结果可以看出,创建StringBuilder的代码被放在了for语句外。虽然这样处理在源程序中看起来复杂,但却换来了更高的效率,同时消耗的资源也更少了。
所以,从上述几个例子中我们得出的结论是:String采用连接运算符(+)效率低下,都是上述循环、大批量数据情况造成的,每做一次"+"就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后append字符串,如此循环直至结束。如果我们直接采用StringBuilder对象进行append的话,我们可以节省创建和销毁对象的时间。如果只是简单的字面量拼接或者很少的字符串拼接,性能都是差不多的。