最佳实践/性能:将StringBuilder。append与String。concat混合使用

90

我想了解不同情况下连接字符串字面量和变量的最佳实践以及原因。例如,如果我有以下代码:

StringBuilder sb = new StringBuilder("AAAAAAAAAAAAA")
    .append(B_String).append("CCCCCCCCCCC").append(D_String)
    .append("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
    .append("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");

这是正确的方法吗?从这篇帖子中,我注意到对字符串使用加号+操作符会创建一个新的StringBuilder实例,并将操作数连接起来,最后返回一个String类型的结果。这似乎比直接调用.append()要多做很多工作。所以如果这是真的,那就不行了。但是,String.concat()呢?每次连接都使用.append()是合适的吗?还是只有变量需要用.append(),而字面值可以用.concat()来连接?

StringBuilder sb = new StringBuilder("AAAAAAAAAAAAA")
    .append(B_String.concat("CCCCCCCCCCC")).append(D_String
    .concat("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
    .concat("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));

在这些情况下,最佳实践和性能的一般规则是什么?我的假设是否正确,+ 应该真的不使用吗?


我认为你基本上已经明白了。关于是否应该使用String +“真的不应该被使用”的观点存在分歧。重要的是你理解后果。 - ControlAltDel
9个回答

189

+ 运算符

String s = s1 + s2

在幕后,这被翻译为:

String s = new StringBuilder(s1).append(s2).toString();

如果你在这里有s1 + s2,想象一下这会增加多少额外的工作:

stringBuilder.append(s1 + s2)

改为:

stringBuilder.append(s1).append(s2)

使用+连接多个字符串

值得注意的是:

String s = s1 + s2 + s3 + ... +sN

被翻译为:

String s = new StringBuilder(s1).append(s2).append(s3)...apend(sN).toString();

concat()

String s = s1.concat(s2);

String 创建一个可以容纳 s1s2char[] 数组。将 s1s2 的内容复制到这个新数组中。实际上比 + 运算符需要的工作要少。

StringBuilder.append()

维护一个内部的 char[] 数组,当需要时它会增长。如果内部的数组足够大,则不会创建额外的 char[] 数组。

stringBuilder.append(s1.concat(s2))

由于s1.concat(s2)会创建一个额外的char[]数组并将s1s2复制到其中,然后将新数组的内容复制到内部的StringBuilderchar[]中,因此性能也比较差。

话虽如此,你应该始终使用append()方法并附加原始字符串(您的第一个代码片段是正确的)。


感谢您的帮助和深入的解释,这正是我所需要的。 - Nick Rolando
12
尽管较新版本的编译器会自动将 + 运算符优化为字符串构建器,但不能假设旧版本也能这样做,因此这并不一定是一件好事。在整个讨论中,忽视了一个非常重要的主题,即 StringBuilder 的主要目的之一:字符串池。使用字符串构建器可以将所有内容保持为 char[],而不像编译器优化之前的 + 运算符那样创建一个中间 String(只进行连接操作)。使用连接操作会创建一个被缓存但未使用的中间 String 对象。 - LINEMAN78
所以,如果我正在对几个不同的字符串进行一次性语句(而不是跨方法的多个语句)连接。在这种情况下,使用+操作符连接String对象而不是为此创建一个StringBuilder是可以的,因为它基本上会执行相同的操作? - Nick Rolando
1
如果我只想连接一个字符,使用 .append('\n') 而不是 .append("\n") 更好吗?因为字符是原始类型,而字符串是对象。 - vrwim
3
《Effective Java第二版》中写道,重复使用字符串拼接运算符来拼接n个字符串需要O(n²)的时间。这个结论目前仍然适用吗? - gkiko
@gkiko:我认为,如果你正在为现代编译器编写代码,那么你可以安全地忽略它。字符串连接优化在Java语言规范1.6中提到(https://docs.oracle.com/javase/specs/jls/se6/html/expressions.html#15.18.1.2),尽管我不确定自从什么时候它实际上被实现在Sun/Oracle VM中。如果这对你真的很重要,在执行String +之前查看生成的字节码,但我认为这是最简单和最可读的方法(而且我已经多年来一直在routine地使用StringBuilder.append :))。 - Franz D.

16
编译器会优化 + 操作符用于字符串拼接的情况。
因此,
int a = 1;
String s = "Hello " + a;

被转换成

new StringBuilder().append("Hello ").append(1).toString();

这里有一个很好的主题 在这里 解释了为什么你应该使用+运算符。


3
由于您使用字面量,编译器会将其优化为String s = "Hello World"; - ColinD
@ColinD:+1。我刚刚修改了我的片段。 - user973999
"Hello " 将在 StringBuilder 的构造函数中。 - Satish Patro

3

编译器会自动完成优化。

Java2编译器会自动转换以下内容:

String s = s1 + s2; 

String s = (new StringBuffer()).append(s1).append(s2).toString();

直接摘自Oracle网站上的Java最佳实践

2

您应该始终使用append

concat会创建一个新的字符串,所以它与+很相似。

如果您对两个最终字符串使用concat+,JVM可以进行优化,因此在这种情况下与执行append相同。


1
如果您要连接两个字符串,请使用String.concat(通过创建一个新的char数组来创建一个新的字符串,该数组适合两个字符串,并将两个字符串的char数组复制到其中)。
如果您在一行中连接多个(超过两个)字符串,请使用+或StringBuilder.append,因为编译器将+转换为StringBuilder.append。对于多个字符串,这很好,因为它维护一个随需增长的字符数组。
如果您在多行上连接多个字符串,请创建一个StringBuilder并使用append方法。最后,在将字符串附加到StringBuilder时,请使用其.toString()方法将其创建为字符串。对于在多行上连接,这比第二种方法更快,因为第二种方法会在每行上创建一个新的StringBuilder,将字符串附加并转换回字符串,而第三种方法仅对整个过程使用一个StringBuilder。

1
我个人更喜欢使用Strings.format(),它是一个简单易读的一行字符串格式化
String b = "B value";
String d = "D value";
String fullString = String.format("A %s C %s E F", b, d);
// Output: A B value C D value E F

0

使用+运算符是最佳实践,也简单易读。

Java语言提供了特殊的支持字符串连接运算符(+),以及将其他对象转换为字符串。字符串连接是通过StringBuilder(或StringBuffer)类及其append方法实现的。

官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html


0
在字节码级别上,它们没有区别,并且我们不会妥协效率。在执行字节码级别时,必须通过调用append来使用非内联运算符重载方法进行+操作。然后,在汇编语言级别(Java是用C编写的,C生成类似汇编的程序),将有额外的寄存器调用以在堆栈中存储+方法调用,并且还将有额外的push。(实际上,交叉编译器可能会优化+运算符调用,在这种情况下,效率不会有任何区别。)
增加可读性的一种好习惯是只有一种方式。 :)

0

所有答案都非常好且有解释性。但是我觉得探索其他字符串连接技术也会有所帮助,例如- Guava Joiner、Streams、String.format等。

有关每种连接技术的完整详细信息,请参见 java-string-concatenation-which-way-is-best

简而言之,连接性能随要连接的字符串数量而变化。例如- 要连接1-10个字符串,这些技术最适合 - StringBuilder,StringBuffer和Plus运算符。 要连接数百个字符串 - Guava Joiner,apache's stringsUtils库也非常有效。

请查看上述博客。它真正很好地解释了性能效率。

谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接