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


86

我试图了解什么是最佳实践,以及为什么要在不同情况下串联字符串文字和变量。例如,如果我有这样的代码

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

这是这样做的方式吗?从这篇文章中,我注意到+Strings的运算符创建了一个StringBuilder的新实例,连接了操作数,并返回了String转换,这似乎比调用还多了很多.append();所以如果这是真的,那是不可能的。但是呢String.concat()?是否适合.append()用于每个串联?或仅用于变量,文字可以追加.concat()吗?

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

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


我想基本上您已经掌握了。对于String +“真的不应该使用”是否有不同的意见。关键是您了解后果
ControlAltDel 2012年

Answers:


184

+ 算子

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创建char[]同时适合s1和的数组s2。将副本s1s2内容复制到此新数组。实际上需要的工作量少于+操作员。

StringBuilder.append()

维护一个内部char[]数组,该数组在需要时会增长。char[]如果内部空间足够大,则不会创建任何额外的空间。

stringBuilder.append(s1.concat(s2))

也表现不佳,因为s1.concat(s2)创建一个额外的char[]数组和副本s1,并s2给它只是新的数组内容复制到内部StringBuilder char[]

话虽这么说,您应该一直使用append()并附加原始字符串(您的第一个代码段是正确的)。


感谢您的帮助和深入的解释;那才是我真正需要的
Nick Rolando'4

12
尽管较新版本的编译器会自动将+运算符优化为字符串生成器,但这并不一定是一件好事,而较早版本则不会。在整个讨论中有一个非常重要的话题被忽略了,实际上这是StringBuilder的主要目的之一,即字符串池。使用字符串生成器会将所有内容保留为char [],并且不会像创建编译器优化之前用来执行concat的+运算符那样创建中间字符串。使用concat会创建一个中间String对象,该对象将被缓存但未使用。
LINEMAN78

因此,如果我要在多个不同的字符串上执行一次语句(一个方法中没有多个语句)的连接。可以+在String对象上使用而不是StringBuilder为此一次串联创建一个,因为它基本上会做同样的事情?
Nick Rolando'4

1
而且,如果我只希望连接一个字符,那么使用.append('\ n')而不是.append(“ \ n”)会更好吗?因为字符是原始类型,而字符串是对象?
vrwim 2013年

3
有效的Java第二版重复使用字符串连接运算符来连接n个字符串,需要n的时间为二次方。仍然有意义吗?
gkiko

16

编译器优化+串联。

所以

int a = 1;
String s = "Hello " + a;

变成

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

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


3
String s = "Hello World";由于您使用文字,因此编译器实际上会将其优化为。
ColinD'4

@ColinD:+1。刚刚修改了我的代码段。

3

优化由编译器自动完成。

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

String s = s1 + s2; 

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

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


2

您应该始终使用append

concat创建一个新的String,就像+我想的那样。

如果与2个最终String一起concat使用+,则JVM可以进行优化,因此与在这种情况下执行append相同。


1

如果您恰好连接了两个String,请使用String.concat(通过创建同时适合两个String的新char数组来创建新String并将两个String的char数组复制到其中)。

如果您在一行中连接多个(两个以上)字符串,请使用+或StringBuilder.append,这无关紧要,因为编译器会将+转换为StringBuilder.append。这对于多个String很有用,因为它可以维护一个按需增长的char数组。

如果在多行上连接多个String,请创建一个StringBuilder并使用append方法。最后,完成将字符串附加到StringBuilder后,使用.toString()方法从中创建一个字符串。对于多行压缩,这比第二种方法要快,因为第二种方法将在每行上创建一个新的StringBuilder,追加String,然后将其强制转换回String,而第三种方法仅对整个对象使用一个StringBuilder。



0

在字节码级别上没有什么不同,我们也没有在这方面降低效率。如果执行字节码级别,则必须通过调用append来通过+的非内联运算符重载方法。然后在汇编语言级别(Java用C语言编写,并且C产生类似于汇编的程序集,将会有额外的寄存器调用来存储+堆栈中的方法调用,并且还会有附加的推送。(实际上,交叉编译器可能会优化+运算符调用,在这种情况下,效率没有任何区别。)

有一种增加可读性的方法是一个好习惯。:)


0

我个人更喜欢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

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

有关每种串联技术的性能的完整详细信息,请参见java-string-concatenation-which-way-best

简而言之,串联性能随否而变化。要连接的字符串。例如,要串联1-10个字符串,这些技术效果最好-StringBuilder,StringBuffer和Plus运算符。为了连接100多个字符串-Guava Joiner,apache的stringsUtils库也很好用。

请浏览以上博客。它确实很好地说明了性能效率。

谢谢。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.