Java:String concat vs StringBuilder - 优化了,我该怎么办?

Posted

技术标签:

【中文标题】Java:String concat vs StringBuilder - 优化了,我该怎么办?【英文标题】:Java: String concat vs StringBuilder - optimised, so what should I do? 【发布时间】:2013-02-02 08:55:21 【问题描述】:

在this answer 中,它说(暗示)字符串连接无论如何都优化为 StringBuilder 操作,所以当我编写代码时,是否有任何理由在源代码中编写 StringBuilder 代码?请注意,我的用例与 OP 的问题不同,因为我正在连接/附加数十万行。

为了让自己更清楚:我很清楚它们之间的差异,只是我不知道是否值得实际编写 StringBuilder 代码,因为它的可读性较差,而且它的表亲 String 类被认为是较慢的无论如何都会在编译过程中自动转换。

【问题讨论】:

这里分析得不错:javacodegeeks.com/2013/03/java-stringbuilder-myth-debunked.html 【参考方案1】:

我认为 StringBuilder+ 的使用实际上取决于您使用它的上下文。

一般使用 JDK 1.6 及以上版本的编译器会自动使用StringBuilder 将字符串连接在一起。

String one = "abc";
String two = "xyz";
String three = one + two;

这会将String three 编译为:

String three = new StringBuilder().append(one).append(two).toString();

这非常有帮助,并为我们节省了一些运行时间。然而,这个过程并不总是最优的。举个例子:

String out = "";
for( int i = 0; i < 10000 ; i++ ) 
    out = out + i;

return out;

如果我们编译成字节码,然后反编译生成的字节码,我们会得到类似的结果:

String out = "";
for( int i = 0; i < 10000; i++ ) 
    out = new StringBuilder().append(out).append(i).toString();

return out;

编译器已经优化了内部循环,但肯定没有做出最好的优化。为了改进我们的代码,我们可以使用:

StringBuilder out = new StringBuilder();
for( int i = 0 ; i < 10000; i++ ) 
    out.append(i);

return out.toString();

现在这比编译器生成的代码更优化,因此在需要高效代码的情况下,肯定需要使用StringBuilder/StringBuffer 类编写代码。当前的编译器不擅长处理循环中的连接字符串,但是这在未来可能会改变。

您需要仔细查看需要手动应用StringBuilder 的位置,并尝试在不会降低代码可读性的地方使用它。

注意:我使用 JDK 1.6 编译代码,并使用 javap 程序反编译代码,该程序会输出字节码。它相当容易解释,并且在尝试优化代码时通常是一个有用的参考。编译器确实会在幕后更改您的代码,所以看看它做了什么总是很有趣!

【讨论】:

您的反编译代码(在第二个 for 循环中)是实际反编译的输出,还是您的猜测?如果您在其中调试带有“+”字符串连接的 for/while 循环,您会发现 StringBuilder 对象不是在每个循环中都重新创建,但实际上相同的对象被重用。否则,如果您要替换一个较重的对象(即 StringBuilder 代替 String),您真正得到的优化是什么? 我也想知道@SpencerKormos 问题的答案。编译器是否真的为每个循环迭代创建一个新的StringBuilder?为什么这是最优的? 重读答案,以及 OP 问题中的链接,Tom 提出的观点是,如果 concat 操作发生在循环中,那么编译器不会对整体代码进行优化块(包括 for 循环)。在这种情况下,您将在 for 循环 之前 声明您的 StringBuilder,并在循环内使用 append(),从而使 concat 操作最有效(即来自 Tom 的最后一个代码块)。希望这是有道理的,感谢您让我回到这个问题! 使用 + 连接字符串将从 java (web.archive.org/web/20041205170952/http://java.sun.com:80/docs/…) 的非常大的地方转向 Stringbuilder 【参考方案2】:

您问题中的关键词是“据说较慢”。您需要确定这是否确实是一个瓶颈,然后看看哪个更快。

如果你要写这段代码,但还没有写,那就写你更清楚的东西,如果有必要,看看它是否是一个瓶颈。

虽然使用您认为更可能更快的代码是有意义的,但如果两者的可读性相同,实际上花时间找出在您不需要时哪个更快是浪费时间。在性能不可接受之前,可读性高于性能。

【讨论】:

实际上,在(只是)双向重写代码之后,可读性几乎没有区别,因为它只是更改了类声明并将 concat() 转换为 append()。我想我会坚持使用 StringBuilder 以确保性能。【参考方案3】:

视情况而定,但 StringBuilder 被认为要快一些。如果您在循环内进行连接,那么我建议您使用 StringBuilder。

无论如何,我建议您对代码进行分析和基准测试(如果您要进行如此大规模的追加)。

但请注意:StringBuilder 的实例是可变的,不能在线程之间共享(除非您真的知道自己在做什么。)而不是不可变的 String。

【讨论】:

重写了问题。我知道你说的,我只是说,我应该写什么? 确实应该有某种证据或来源来支持“StringBuilder 被认为更快一点”。我认为这是因为缺少同步开销,但仍然 - 来源会很好。【参考方案4】:

我可能误解了您的问题,但 StringBuilder 在附加字符串时更快。所以,是的,如果您要附加“数十万行”,您绝对应该使用 StringBuilder(如果您正在运行多线程应用程序,则使用 StringBuffer)。

(在 cmets 中更彻底的答案)

【讨论】:

显然现代/成熟的 Java 编译器足够聪明,可以将连接操作转换为 StringBuilder 追加操作,所以在代码中使用 StringBuilder 的意义何在,因为它的可读性较差,并且无论如何将 String 连接转换为 StringBuilder 操作? 哦,现在我明白你的意思了,伙计。是的,你是对的:编译器会发现有机会优化你的代码,并且大多数时候会用 StringBuilder 替换常规的 String.concat - 但并非总是如此。您不能依赖编译器。如果代码很复杂,编译器很可能会错过优化的机会。如果我是你,为了确保优化,我会选择让我的代码可读性差一点。

以上是关于Java:String concat vs StringBuilder - 优化了,我该怎么办?的主要内容,如果未能解决你的问题,请参考以下文章

Java中String连接性能的分析

java字符串拼接的几种方式

java字符串拼接的几种方式

Java中StringStringBuffer和StringBuilder的区别

字符串连接中 string.concat 和 + 运算符之间的区别[重复]

Java中StringStringBuffer和StringBuilder比较