多个System.out.print()和字符串连接的区别

9

基本上,我在想哪种方法是更好的实践方法,

for(int i = 0; i < 10000; i++){
    System.out.print("blah");
}
System.out.println("");

或者

String over_9000_blahs = "";
for(int i = 0; i < 10000; i++){
    over_9000_blahs += "blah";
}
System.out.println(over_9000_blahs);

还有没有更好的方法我不知道吗?

当输出被缓冲时,直接输出会更快。字符串的连接需要重复复制字符串,速度会非常慢(使用字符串构建器可以加快速度)。 - that other guy
1
@luk2303 在编译器内部,对String类型的+=操作符会被转换为StringBuilder。https://dev59.com/JnVD5IYBdhLWcg3wOo9h#47628 - jseashell
2
那么,适用于未明确要求的需求(无限循环)就能使一种方法普遍更好吗?不是这样的。 - Mark Adelsberger
好的,它不需要像第二个选项中所述那样产生巨大的中间状态。如果增加迭代次数或减少堆的大小会怎么样?如何预测? - Lyubomyr Shaydariv
3
在循环中,每次迭代都会创建一个新的StringBuilder实例 - 这不是一个好主意。如果您在链接的帖子上向下滚动,就会看到这一点。您链接的实际答案与此问题无关,但ckpwong的回答是相关的。在那里,您可以清楚地阅读手动使用StringBuilder是正确的方法(而且两个答案基本上说了同样的事情)。 - luk2302
显示剩余3条评论
5个回答

6

如果您只是写入System.out,那么第一种方法更好。但是,如果性能对您很重要,请使用下面的方法(System.out.println已同步并使用锁定 - 可以在此处此处了解更多信息)。

如果您想稍后使用“大字符串”或提高性能,则使用StringBuilder更为清晰。 (请参见下文),无论如何,字符串+都将由编译器转换为StringBuilder(更多详细信息请参见此处)。

        StringBuilder stringBuilder = new StringBuilder();
        for(int i = 0; i < 10000; i++){
            stringBuilder.append("bla");
        }
        System.out.println(stringBuilder.toString());

1
@Mzf 在 String 上的 += 会在编译器内部被转换为 StringBuilder。https://dev59.com/JnVD5IYBdhLWcg3wOo9h#47628 - jseashell
1
@j.seashell 但据我所知,+= 操作符会在每次迭代时创建一个新的 StringBuilder 对象。编译器可能会进行进一步的优化,但并不保证。 - Hulk
你很幸运,因为“i”并不比10000更大。 - Lyubomyr Shaydariv

6

如果你在一个循环中连接字符串(数量较大),则应使用 StringBuilder

for(int i = 0; i < 10000; i++){
    over_9000_blahs += "blah";
}

这段代码的作用是在每次迭代中执行以下操作:
  • 在内部创建一个新的StringBuilder,其内部char数组足够大以容纳中间结果(over_9000_blahs
  • 将字符从over_9000_blahs复制到内部数组
  • 复制字符从"blah"
  • 创建一个新的String,再次从内部数组复制字符

因此,每次迭代都会有两个越来越长的字符串副本 - 这意味着二次时间复杂度。


由于System.out.println() 可能是同步的,所以调用它多次可能比使用StringBuilder慢(但我猜想使用+=在循环中连接字符串不会比其慢)。

因此,StringBuilder方法应该是这三种方法中最好的一种。


2
按性能顺序排列:
1. StringBuilder是最快的。基本上,它只是将单词添加到字符数组中。当容量不够时,就会进行乘法运算。不应该超过log(10000)次。
2. System.out.print与StringBuilder相比表现较差,因为我们需要锁定输出10000次。此外,在print选项中,会创建新的char[writeBufferSize] 10000次,而在StringBuilder选项中,我们仅执行1次!
3. 字符串连接。创建许多(后来还有很大的)对象,从某个“i”开始,内存管理会严重影响性能。
编辑:
更准确地说,因为问题是关于第二个和第三个选项之间的区别,而且很清楚为什么StringBuilder很快。
我们可以说第二种方法中的每次迭代都需要K时间,因为代码相同,每次迭代的字符串长度也相同。在执行结束时,第二个选项将花费10000 * K时间进行10000次迭代。对于第三个选项,我们不能这样说,因为每次迭代的字符串长度都在增加。因此,分配对象和垃圾收集所需的时间会增加。我想说的是,第三个选项的执行时间并不是线性增加的。因此,对于低NumberOfIterations,我们可能看不出后两个方法之间的差异。但是我们知道,从特定的NumberOfIterations开始,第二个选项总是优于第三个选项。

0
在这种情况下,我会说第一个更好。Java使用StringBuilder来进行字符串拼接以提高性能,但由于Java不知道你在第二种情况中重复使用循环进行拼接,所以第一个情况会更好。

0
如果你只想输出你的值 - 结果是一样的。
第二个选项会在内存中创建许多字符串,这些字符串将由GC(垃圾回收器)处理。(但在较新版本的Java中,这个问题不会发生,因为连接将在幕后转换为下面的StringBuilder解决方案)
如果你想在sysout之后再使用你的字符串,你应该检查StringBuilder类和append方法:
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 10000; i++){
    sb.append("blah");
}
System.out.println(sb);

不确定您是否正确(关于GC),请看这里:https://dev59.com/omkw5IYBdhLWcg3wMHgY 。在幕后,它被转换为StringBuilder。 - Mzf
1
在所有情况下都不是真的。但在大多数情况下,你是正确的。 - ByeBye
在字符串 String 上使用 += 会被编译器转换为 StringBuilder - jseashell
1
当谈到最佳实践时(不要创建不必要的字符串),无论如何,+=在某些Java版本中会在后台转换为StringBuilder。尽管如此,概念是相同的。这个答案应该更新以披露StringBuilder有时被秘密使用,然后每个人都可以感到高兴。 - Michael Yaworski

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