一、简单介绍
编写代码过程中,使用"+"和"contact"比较普遍,但是它们都不能满足大数据量的处理,一般情况下有一下四种方法处理字符串拼接,如下:
1、 加号"+"
2、 String
的 contact()
方法
3、 StringBuffer
的 append()
方法
4、 StringBuilder
的 append()
方法
二、测试代码参考
StringTest
类
package com.jdk8.event.StringTest;
public class StringTest {
private static final int max = 100000;
public static void testPlus() {
System.out.println("************ plusTest() ************");
String str = "";
long start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
str = str + "a";
}
long end = System.currentTimeMillis();
long cost = end - start;
System.out.println(" {str + \"a\"} cost=" + cost + " ms");
}
public static void testConcat() {
System.out.println("************ concatTest() ************");
String str = "";
long start = System.currentTimeMillis();
for (int i = 0; i < max; i++) {
str = str.concat("a");
}
long end = System.currentTimeMillis();
long cost = end - start;
System.out.println(" {str.concat(\"a\")} cost=" + cost + " ms");
}
public static void testStringBuffer() {
System.out.println("************ StringBufferTest() ************");
long start = System.currentTimeMillis();
StringBuffer strBuffer = new StringBuffer();
for (int i = 0; i < max; i++) {
strBuffer.append("a");
}
strBuffer.toString();
long end = System.currentTimeMillis();
long cost = end - start;
System.out.println(" {strBuffer.append(\"a\")} cost=" + cost + " ms");
}
public static void testStringBuilder() {
System.out.println("************ StringBuilderTest() ************");
long start = System.currentTimeMillis();
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < max; i++) {
strBuilder.append("a");
}
strBuilder.toString();
long end = System.currentTimeMillis();
long cost = end - start;
System.out.println(" {strBuilder.append(\"a\")} cost=" + cost + " ms");
}
}
测试类:TestMain
package com.jdk8.event.StringTest;
public class TestMain {
public static void main(String[] args){
System.out.println("执行100次时");
StringTest.testPlus();
StringTest.testConcat();
StringTest.testStringBuffer();
StringTest.testStringBuilder();
}
}
测试结果如下:
执行100次时
************ plusTest() ************
{str + "a"} cost=0 ms
************ concatTest() ************
{str.concat("a")} cost=0 ms
************ StringBufferTest() ************
{strBuffer.append("a")} cost=0 ms
************ StringBuilderTest() ************
{strBuilder.append("a")} cost=0 ms
执行1000次时
************ concatTest() ************
{str.concat("a")} cost=1 ms
************ StringBufferTest() ************
{strBuffer.append("a")} cost=0 ms
************ StringBuilderTest() ************
{strBuilder.append("a")} cost=0 ms
执行10000次时
************ plusTest() ************
{str + "a"} cost=99 ms
************ concatTest() ************
Disconnected from the target VM, address: '127.0.0.1:63818', transport: 'socket'
{str.concat("a")} cost=17 ms
************ StringBufferTest() ************
{strBuffer.append("a")} cost=1 ms
************ StringBuilderTest() ************
{strBuilder.append("a")} cost=0 ms
执行100000次时
************ plusTest() ************
{str + "a"} cost=6176 ms
************ concatTest() ************
{str.concat("a")} cost=1267 ms
************ StringBufferTest() ************
{strBuffer.append("a")} cost=3 ms
************ StringBuilderTest() ************
Disconnected from the target VM, address: '127.0.0.1:63841', transport: 'socket'
{strBuilder.append("a")} cost=2 ms
执行300000次时
************ plusTest() ************
{str + "a"} cost=23554 ms
************ concatTest() ************
{str.concat("a")} cost=17667 ms
************ StringBufferTest() ************
{strBuffer.append("a")} cost=6 ms
************ StringBuilderTest() ************
{strBuilder.append("a")} cost=5 ms
执行500000次时
************ plusTest() ************
{str + "a"} cost=61421 ms
************ concatTest() ************
{str.concat("a")} cost=53173 ms
************ StringBufferTest() ************
{strBuffer.append("a")} cost=9 ms
************ StringBuilderTest() ************
{strBuilder.append("a")} cost=7 ms
1、 方法1 "+"号和方法2 contact()
方法适用于数据量比较小的情况下。为了操作代码风格和养成习惯,还是建议尽量不用
2、方法2 StringBuffer
的 append()
方法和StringBuilder
的 append()
方法 本质上是一样的,均继承自AbstractStringBuilder
抽象类,效率很高,数据量比较大的时候的推荐用法。区别是StringBuffer
是线程安全的,而StringBuilder
是线程不安全的。
三、源代码分析
1、 String
的"+"方法,编译器对其做了优化,使用StringBuilder
的append()
方法进行追加,但是每循环一次都会创建一个StringBuilder
对象,且都会调用toString()
方法转换为字符串,因此开销很大。
注:执行一次String
的"+"方法,相当于str = new StringBuilder(str).append("x").toString()
;
2、调用String
的contact()
方法,其实就是一次数组的拷贝,虽然在内存中的处理都是原子操作,速度很快,但是当返回时是要创建一个新的String对象的,这样不仅限制了效率,更添加了空间的压力,源码如下:
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
3、StringBuffer
和StringBuilder
的append()
方法均主要使用父类AbstractStringBuilder
的append()
方法。源码如下:
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
即当数组空间够用的时候,只是在数组后面添加字符或字符串,并不创建新的对象,只是到最后通过toString()
方法来生成最终的字符串,效率很快,也很节省空间,是推荐的用法。