0%

Java字符串拼接效率比较

字符串拼接的三种方法

  1. 加号

  2. concat方法

  3. StringBuilder(或StringBuffer)的append方法

程序例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class Main {
public static void main(String[] args) {
String str = "a";
long time = System.currentTimeMillis();
for (int i = 0; i < 50000; i++) {
str += "c";
}
System.out.println("加号所花费的时间:");
System.out.println(System.currentTimeMillis()-time);
String str2 = "a";
time = System.currentTimeMillis();
for (int i = 0; i < 50000; i++) {
str2.concat("c");
}
System.out.println("cancat方法所花费的时间:");
System.out.println(System.currentTimeMillis()-time);
time = System.currentTimeMillis();
StringBuilder stringBuilder = new StringBuilder("a");
for (int i = 0; i < 50000; i++) {
stringBuilder.append("c");
}
String str3 = stringBuilder.toString();
System.out.println("StringBuilder的append方法:");
System.out.println(System.currentTimeMillis()-time);
}
}

程序输出

5cc54b2b87ac05cc54b2b87ac0

append方法最快、concat次之、加号最慢

“+”方法

虽然编译器对字符串的加号做了优化,它会使用StringBuilder的append方法进行追加,而它最终通过toString方法转换成String字符串,上例中“+”拼接的代码即如下:

1
str = new StringBuilder(str).append("JTZen9").toString();

它与纯粹地使用StringBuilder的append方法是不同的:

  1. 每趟循环都会创建一个StringBuilder对象

  2. 每次执行完毕都会调用toString方法将其转换为字符串

所以,就耗费了更多的时间。

concat方法

concat源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public String concat(String str) {
// 追加的字符串长度为0
int otherLen = str.length();
// 如果追加的字符串长度为0,则返回原字符串本身
if (otherLen == 0) {
return this;
}
// 获取原字符串的字符数组的长度
int len = value.length;
// 将原字符串的字符数组放到buf数组中
char buf[] = Arrays.copyOf(value, len + otherLen);
// 追加的字符串转化成字符数组,添加到buf中
str.getChars(buf, len);
// 产生一个新的字符串
return new String(buf, true);
}

整体是一个数组的拷贝,虽然在内存中是处理都是原子性操作,速度非常快,但是,最后的return语句创建一个新String对象,也就是每次concat操作都会创建一个新的String对象,这也是限制concat方法速度的原因。

append方法

append源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public AbstractStringBuilder append(String str) {
// 如果是null值,则把null作为字符串处理
if (str == null)
return appendNull();
int len = str.length();
// 追加后的字符数组长度是否超过当前值
ensureCapacityInternal(count + len);
// 字符串复制到目标数组
str.getChars(0, len, value, count);
count += len;
return this;
}
private AbstractStringBuilder appendNull() {
int c = count;
ensureCapacityInternal(c + 4);
final char[] value = this.value;
value[c++] = 'n';
value[c++] = 'u';
value[c++] = 'l';
value[c++] = 'l';
count = c;
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity); // 加长,并作数组拷贝
}

整个append方法都在做字符数组的处理,加长,拷贝等,这些都是基本的数据处理,整个方法内并没有生成对象。只是最后toString返回一个对象而已。

题外

1
2
String str = "My name is ";
str = str + "JTZen9";

相当于 str = new StringBuilder(str).append(“JTZen9”).toString(); 也就是说,该str = str + “JTZen9”;语句执行完之后,总共有三个对象。

1
String str = "My name is " + "JTZen9";

JVM会直接把str作为一个对象,即 “My name is JTZen9”

使用场景

  1. 大多数情况,我们使用“+”,符合编码习惯和我们的阅读

  2. 当在频繁进行字符串的运算(如拼接、替换、删除等),或者在系统性能临界的时候,我们可以考虑使用concat或append方法