三种方法
java字符串连接有三种方法:
- 用加号+连接,如:
"abc"+"bcd"
- String对象的concat方法,如:
"abc".concat("bcd")
- 以及StringBuffer或StringBuilder类,如:
StringBuilder stringBuilder = new StringBuilder ("abc");
stringBuilder.append ("bcd");
stringBuilder.toString ();
StringBuffer和StringBuilder都是继承于AbstractStringBuilder的,这两个类在API和对AbstractStringBuilder的实现都是非常相似的。最主要的区别还是在于StringBuffer使用了synchronized来保证线程安全,而StringBuilder则不适用于多线程环境。
效率对比
用来测试的代码:
long startTime = System.currentTimeMillis ();
String str = "";
StringBuilder stringBuilder = new StringBuilder ();
for (int i = 0; i < 500000; i++) {
stringBuilder.append ("a ");
}
str = stringBuilder.toString ();
long elapsedMillis = System.currentTimeMillis () - startTime;
System.out.println ("elapsedMillis=" + elapsedMillis);
经过反复测试之后三种方法的效率对比:
StringBuilder >> concat > +
可以看出在如上的测试代码上,StringBuilder的效率远超其他两种。
但是,并不能说明在任何情况下,StringBuilder的效率都绝对超过加号和concat
,具体原因且看下面的分析。
源码分析
String类内部是维护了一个char[]字符数组的,字符串拼接的本质就是创建一个可以容纳下两个旧char[]的新char[],再将两个String内部的char[]数组分别copy过去,数组copy肯定最后用的都是System.arraycopy。
既然原理都是一样的,那为什么上面的测试中StringBuilder的性能要明显优于其他两种方法呢?并且java规范中明确说了,加号就是用StringBuffer或StringBuilder实现的。
原因就在于创建char[]对象的个数不同。
concat方法
每调用一次concat就会创建一个新String,新String内部char[]长度正好等于两个旧String的长度之和,循环50W次就要创建50W个char[]!
StringBuffer和StringBuilder
StringBuffer和StringBuilder则不一样,它们内部维护了一个可变长的数组。
可变长并不是指这个数组对象长度可变,数组对象一经创建长度就是固定的。可变长的数组实际上是指这个数组引用没被final
修饰符修饰,可以让它指向长度更长的数组对象,这个过程叫扩容。String 内部数组不可变长的原因是用了final修饰。
数组的初始容量是大于你传入的字符串的长度的(多16),每次append就是将String的char[] copy到StringBuffer和StringBuilder的内部数组里面。空间不够用了就会进行扩容, 每次扩容新容量 = 旧容量 x 2 + 2
,这样就可以大大减少扩容次数,即减少创建数组的数量。这就是在大量数据的情况下StringBuilder效率远超concat的原因。
但是如果仅仅是两个字符串进行拼接的话,concat可能效率更高。
加号
至于加号,每一句str += "a ";
会在编译期间被替换成:
str = new StringBuilder (str).append("a");
"a"+"a"+"a"
被替换成:
new StringBuilder (str).append("a").append("a").append("a");
也就是每一句包含加号的语句都会new一个StringBuilder,加号用append替换。
对应到上面的测试程序,每循环一次就会创建一个StringBuilder 对象,效率自然不高。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)