StringBuilder / StringBuffer与“+”运算符

时间:2011-01-10 15:50:38

标签: java performance readability

我正在阅读“更好,更快,更轻松的Java ”(由Bruce Tate和Justin Gehtland撰写)并且熟悉敏捷类型团队的可读性要求,例如Robert Martin在他的讨论中所讨论的内容清洁编码书。在我现在的团队中,我被告知明确不要使用+运算符,因为它会在运行时创建额外的(和不必要的)字符串对象。

但是这个article,写在'04讨论对象分配是关于10个机器指令。 (基本上是免费的)

它还讨论了GC如何帮助降低此环境中的成本。

使用+StringBuilderStringBuffer之间的实际性能权衡是什么? (在我的例子中,它仅为StringBuffer,因为我们仅限于Java 1.4.2。)

StringBuffer对我来说导致丑陋,不太可读的代码,正如Tate的书中的几个例子所示。并且StringBuffer是线程同步的,它似乎有自己的成本超过使用+运算符的“危险”。

思想/意见?

4 个答案:

答案 0 :(得分:47)

编译器将String连接转换为StringBuilder操作。

要了解编译器的工作方式,我将采用一个示例类,编译它并使用jad对其进行反编译,以查看生成的字节码是什么。

原班级:

public void method1() {
    System.out.println("The answer is: " + 42);
}

public void method2(int value) {
    System.out.println("The answer is: " + value);
}

public void method3(int value) {
    String a = "The answer is: " + value;
    System.out.println(a + " what is the question ?");
}

反编译类:

public void method1()
{
    System.out.println("The answer is: 42");
}

public void method2(int value)
{
    System.out.println((new StringBuilder("The answer is: ")).append(value).toString());
}

public void method3(int value)
{
    String a = (new StringBuilder("The answer is: ")).append(value).toString();
    System.out.println((new StringBuilder(String.valueOf(a))).append(" what is the question ?").toString());
}
  • method1上,编译器在编译时执行了该操作。
  • method2 String级联相当于手动使用StringBuilder
  • method3上,String连接肯定是错误的,因为编译器正在创建第二个StringBuilder而不是重用前一个{.1}。

所以我的简单规则是连接是好的,除非你需要再次连接结果:例如在循环中或当你需要存储中间结果时。

答案 1 :(得分:21)

您的团队需要了解reasons for avoiding repeated string concatenation

当使用StringBuffer时,确实 次 - 特别是当你在循环中创建一个字符串时,特别是如果你不是确定< / em>循环中会有很少的迭代。请注意,这不仅仅是创建新对象的问题 - 而是复制已经附加的所有文本数据。还要记住,如果不考虑垃圾收集,对象分配只是“基本上免费”。是的,如果当前一代有足够的空间,那基本上就是增加一个指针......但是:

  • 记忆必须在某个时候被清除。这不是免费的。
  • 您需要缩短时间,直到需要下一个GC。 GC不是免费的。
  • 如果您的物体生活在下一代,可能需要更长时间才能清理 - 再次,不是免费的。

所有这些东西都是相当便宜因为它“通常”不值得将设计从优雅中剔除以避免创建对象......但你不应该将它们视为免费

另一方面,在不需要中间字符串的情况下使用StringBuffer没有意义。例如:

String x = a + b + c + d;

至少和以下一样有效:

StringBuffer buffer = new StringBuffer();
buffer.append(a);
buffer.append(b);
buffer.append(c);
buffer.append(d);
String x = buffer.toString();

答案 2 :(得分:5)

对于小型连接,您可以简单地使用String和+以便于阅读。性能不会受到影响。但是如果你正在进行大量的连接操作,那就去StringBuffer。

答案 3 :(得分:0)

其他答案提到在循环中创建字符串时应使用StringBuilder。但是,大多数循环都是遍历集合,而从Java 8可以使用joining类中的Collectors方法将集合转换为字符串。

作为示例,在下面的代码中:

String result = Arrays.asList("Apple", "Banana", "Orange").stream() 
                     .collect(Collectors.joining(", ", "<", ">")); 

result将是:<Apple, Banana, Orange>