StringBuilder / StringBuffer与“+”运算符

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

但是这篇文章 ,在'04年写回来谈到如何对象分配是关于10个机器指令。 (基本上免费)

它还谈到GC如何帮助降低这种环境下的成本。

什么是使用+StringBuilderStringBuffer之间的实际性能折衷? (在我的情况下,它只是StringBuffer因为我们仅限于Java 1.4.2。)

对于我来说, StringBuffer结果是丑陋的,可读性较差的代码,正如Tate书中的几个例子所演示的那样。 而且StringBuffer是线程同步的,似乎有自己的成本超过使用+运算符的“危险”。

思想/意见?

使用String concatenation被编译器翻译成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()); } 
  • 在方法1上,编译器在编译时执行该操作。
  • method2String连接相当于手动使用StringBuilder
  • method3上,由于编译器正在创build第二个StringBuilder而不是重复使用前一个,因此String串联绝对是不好的。

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

您的团队需要了解避免重复string连接的原因 。

当然有时候使用StringBuffer是有意义的,特别是当你在循环中创build一个string的时候,特别是如果你不确定循环中会有很less的迭代。 请注意,这不仅仅是创build新对象的问题,而是复制已经附加的所有文本数据。 另外请记住,如果您不考虑垃圾回收,对象分配只是“基本上免费的”。 是的,如果现在有足够的空间,基本上是增加一个指针的问题…但是:

  • 那个记忆在某个时刻一定已经被清除了。 这不是免费的。
  • 您正在缩短时间,直到需要下一个GC。 GC不是免费的。
  • 如果你的客体进入下一代,可能需要更长的时间来清理 – 再次,不是免费的。

所有这些东西都相当便宜 ,因为它通常不值得将devise从高雅上弯曲,以避免创造出物体,但是你不应该把它们视为自由

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

 String x = a + b + c + d; 

至less和以下一样高效:

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

对于小的连接,您可以简单地使用String和+来提高可读性。 性能不会受到影响。 但是,如果你正在做大量的串联操作,那就去StringBuffer吧。

考虑下面的性能testing,我们正在100000个迭代循环中构build一个dynamicstring:

 public void constructDynamicString() { long startTime = System.currentTimeMillis(); String s = "test"; //StringBuffer s = new StringBuffer("test"); //StringBuilder s = new StringBuilder("test"); for(int i=0; i<100000; i++) { s += "concat"; //s.append("concat"); } long endTime = System.currentTimeMillis(); System.out.println("Concat time ====== " + (endTime - startTime)); } 

我们每次运行上述testing3次,每次都会得到如下结果:

 With String, it tooks: 39068 ms With StringBuffer, it tooks: 7ms With StringBuilder, it tooks: 4ms 

从上面的结果可以看出,使用String对象创builddynamicstring比使用StringBufferStringBuilder要慢,

此外, StringBuilderStringBuffer快,因为它不关心同步,(尽pipe看起来稍微快一些,如果我们增加迭代次数,速度会相对不同)。

现在,要决定使用哪个类,请考虑以下因素:

  1. 如果你正在创build一个不应该在程序stream程中被修改的静态string,那么就使用String对象。
  2. 如果你正在创build一个需要在多个线程之间共享的dynamicstring,那么考虑使用StringBuffer
  3. 如果两个因素都不存在(这是一个非常常见的情况),那就使用StringBuilder

来源: StringBuilder VS StringBuffer