Java编译器构建字符串连接的代码效率低下?

3
我认为这是最有效的字符串拼接方法。
new StringBuilder(s1.length() + s2.length() + s3.length()).append(s1).append(s2).append(s3).toString();

StringBuilder的缓冲区在此处创建,其容量足以容纳其中的所有字符串,否则StringBuilder可能需要将缓冲区扩展3倍。
但是当我编译/反编译这个东西时
String s4 = s1 + s2 + s3;

我得到了由javac(1.7.0_03)构建的实际代码,如下所示:

String s4 = (new StringBuilder(String.valueOf(s1))).append(s2).append(s3).toString();

哪种方式更有效?

更新

正如Steven Schlansker所建议的那样,这里有一个性能测试。

                String s1 = "0123456789";
                String s2 = s1 + s1 + s1;
                String s3 = s1 + s1 + s1 + s1 + s1 + s1;
                long t0 = System.currentTimeMillis();
                for (int i = 0; i < 500000; i++) {

                String s4 = new StringBuilder(s1.length() + s2.length() + s3.length()).append(s1).append(s2).append(s3).toString();

//          String s4 = s1 + s2 + s3;

                }
                System.out.println(System.currentTimeMillis() - t0);

虽然不完美,但结果表明我的版本速度比原版快了约30%。在我的笔记本电脑(Celeron 925)上,对于版本1,它提供了大约230毫秒的时间,而对于版本2,则为大约300毫秒。实际上,这是我预期的结果。因此,我认为如果javac以更高效的方式编译字符串连接,那将是一个好主意。有足够的类似代码行。

return "\\Q" + s + "\\E";

即使在JDK类中也是如此。最后一行来自java.util.Pattern,它是为了效率而设计的。


如果你想要效率,我会使用 Pattern.compile(s, Pattern.LITERAL);。这样只需要针对每个字符串s执行一次,避免了创建StringBuilder和解析字符串的过程。 - Peter Lawrey
1个回答

4

根据你运行它的环境而定。没有一种能够明确比另一种更好,除非进行某种测试。

话虽如此,我同意提前知道最终大小可能会带来一些好处。

我猜测,如果你编写一个微基准测试,你会发现在几乎任何应用程序中差异都是微不足道的。

基准测试建议的编辑:

我看到你在帖子中添加了一些实际数据。恭喜!你正在正确的轨道上 :-)

不幸的是,Java有许多对编写正确基准测试不利的功能。如果你想要你的结果准确,你应该看一下如何在Java中编写正确的微基准测试?。同时,有一点值得考虑:在这种情况下看起来很大的速度提升,在“真实”程序的背景下将会显得微不足道。单个数据库获取(以毫秒为单位)可以轻松地使内存分配中的任何收益完全不重要(以纳秒为单位)。


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