原文链接:java中,字符串拼接方式与效率分析
由于String对象时不可变对象,因此在需要对字符串进行修改操作时(如字符串连接和替换),String对象总是会生成新的对象,所以其性能相对较差。
String常量的累加操作:对于静态字符串的连接操作,Java在编译时会进行彻底的优化,将多个连接操作的字符串在编译时合成一个单独的长字符串。
其实String变量的累加操作:底层使用了StringBuilder
的功能。
StringBuffer
和StringBuilder
的扩容策略:当字符串缓冲区容量不足时,原有容量将会加倍,以新的容量来申请内存空间,建立新的char数组,然后将原数组中的内容复制到这个新的数组当中。因此,对于大对象的扩容会涉及大量的内存复制操作。所以,如果能够预先评估StringBuilder
或StringBuffer
的大小,将能够有效的节省这些操作,从而提高系统的性能。
JAVA的字符串拼接与性能
字符串拼接方法
+
,join
,StringBuffer
,StringBuilder
或String.concat()
- String 是final对象,不会被修改,每次使用 + 进行拼接都会创建新的对象,而不是改变原来的对象,也属于线程安全的;
StringBuffer
可变字符串,主要用于字符串的拼接,属于线程安全的;(StringBuffer
的append()
操作用了synchronized)StringBuilder
可变字符串,主要用于字符串的拼接,属于线程不安全的;
性能分析
循环中循环10000次字符串拼接:
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
public class TestString {
public static void main(String[] args) {
//plus拼接字符串方式
String s = "";
long ts = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
s = s + String.valueOf(i);
}
long te = System.currentTimeMillis();
System.out.println("Plus cost {"+( te - ts) +"} ms");
//concat拼接字符串方式
String s2 = "";
long ts2 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
s2 = s2.concat(String.valueOf(i));
}
long te2 = System.currentTimeMillis();
System.out.println("concat cost {"+(te2 - ts2)+"} ms");
//StringUtils.join拼接字符串方式
List<String> list = new ArrayList<String>();
long ts3 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
list.add(String.valueOf(i));
}
StringUtils.join(list, "");
long te3 = System.currentTimeMillis();
System.out.println("StringUtils.join cost {"+(te3 - ts3)+"} ms");
//StringBuffer拼接字符串方式
StringBuffer sb = new StringBuffer();
long ts4 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
sb.append(String.valueOf(i));
}
sb.toString();
long te4 = System.currentTimeMillis();
System.out.println("StringBuffer cost {"+(te4 - ts4)+"} ms");
//StringBuilder拼接字符串方式
StringBuilder sb5 = new StringBuilder();
long ts5 = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
sb5.append(String.valueOf(i));
}
sb5.toString();
long te5 = System.currentTimeMillis();
System.out.println("StringBuilder cost {"+(te5 - ts5)+"} ms");
}
}
效率对比:
plus cost {249} ms
concat cost {115} ms
StringUtils.join cost {20} ms
StringBuffer cost {1} ms
StringBuilder cost {1} ms
结果分析:
StringBuilder
性能比StringBuffer
要好点。在1千万的循环下,StringBuilder
大约在500-600毫秒,而StringBuffer
大约在700-800毫秒StringUtils.join
方式性能也是可以的,在循环不是很大的情况下,该种方式也是可以考虑的;- 另外2种方式,在循环的下,尽量不要使用。
总结:
- 用+的方式效率最差,
concat
由于是内部机制实现,比+的方式好了不少。 join
和StringBuffer
,相差不大,join
方式要快些,可见这种JavaScript中快速拼接字符串的方式在Java中也非常适用。StringBuilder
的速度最快,但其有线程安全的问题,而且只有JDK5
及以上的版本支持。- 单线程下字符串的串联用
StringBuilder
,多线程下字符串的串联用StrngBuffer
。 - 在编译阶段就能够确定的字符串常量,完全没有必要创建
String
或StringBuffer
对象。直接使用字符串常量的"+"连接操作效率最高(如:String str = “a” + “b” + “c”;
)。
**注:**执行一次字符串“+”,相当于
str = new StringBuilder(str).append("a").toString()
。