在项目中拼接字符串有很多种方式,使用不当的话很容易对性能造成影响,下面就讲讲入如何优化项目中的字符拼接代码。
优化 + 拼接操作
最常用的就是 + 操作了,+ 其实属于 Java 的语法糖:在编译阶段,编译器会将 + 操作编译为 StringBuilder,比如下面的代码:
1 | String a = "Hello "; |
利用 javap 进行一下反编译,可以看到 String c = a + b 这行代码被编译为了 StringBuilder 的 append 操作:
hexo
因此,与 StringBuilder 一样,应该避免在代码中使用如下的字符拼接方式:
1 | String a = ""; |
上面这种优化方式是网上经常提到的,那么除了这种场景以外,项目中其他的 + 字符是否还需要优化呢?这个疑惑在这篇文章中找到了答案:java 字符串拼接的几种方式详解(执行效率及内存占用等对比):
由于编译器会帮助我们优化,那加号的字符串拼接操作可否认为等同 StringBuilder 的使用呢?
答案是这种认知是错误的,主要是以下两点原因:
- 如果加号拼接是多次分开操作的,其实相当于多次实例化了 StringBuilder 对象;
- StringBuilder 的构造方法有 4 个,加号拼接操作优化成调用 StringBuilder 的默认无参构造方法,和实际使用其它构造方法会有区别。
如果使用场景避免如上两个 case,那么其实两种方式是一样的。
结合上面的分析,说说我的个人理解。如果项目中的字符串拼接出现了如下的场景,那么需要使用 StringBuilder 来进行优化:
- 循环中使用
+拼接字符串,或者+是分多次进行拼接的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 循环中拼接
String a = "";
for (int i = 0; i < 10; i++) {
// 每次都会new一个StringBuilder
a += i;
}
// 多次拼接
String s1 = "A";
String s2 = "B";
// new一个StringBuilder
String first = s1 + s2;
String s3 = "C";
// new第二个StringBuilder
String second = first + s3; +拼接的字符串长度很长,会导致多次扩容操作。那么需要使用带参的StringBuilder构造函数进行优化。1
2
3
4String s1 = "sdfghjklzxcvnmsdjsfjksfsfsjf";
String s2 = "sdjkjgdfjgldkdjldkjgldkgjdljkdgkdjgldjdglkdl";
// StringBuilder无参构造函数默认长度是16,在append()时若长度不够会进行扩容
String s3 = s1 + s2;
除上面说的两种情况外,其他的 + 拼接场景可以沿用。 + 本身就是 Java 的一个语法糖,如果性能不会受到影响,那就没有必要过度优化。
复用 StringBuilder 对象
另外,我们还可以利用 StringBuilder 的 setLength(0) 或 delete(0, length) 方法来优化循环中多次创建 StringBuilder 的代码,如下:
1 | List<String> list = new LinkedList<>(); |
setLength(0) 和 delete(0, length) 都可以清空 String 对象,区别是 delete() 操作多执行了一次 cp 数组拷贝操作,而 setLength() 只是重置了 count,源码如下:
1 | // setLength() |
1 | // delete() |
对比实验见:StringBuffer清空操作delete和setLength的效率对比分析
参考文章
String、StringBuilder、StringBuffer