StringBuilder / StringBuffer与“+”运算符
我正在阅读“ 更好,更快,更轻的Java ”(由Bruce Tate和Justin Gehtland撰写),并且熟悉敏捷类团队的可读性要求,例如Robert Martin在其干净的编码书中所讨论的内容。 在现在的团队中,我被明确地告诉不要使用+
运算符,因为它在运行时创build额外的(和不必要的)string对象。
但是这篇文章 ,在'04年写回来谈到如何对象分配是关于10个机器指令。 (基本上免费)
它还谈到GC如何帮助降低这种环境下的成本。
什么是使用+
, StringBuilder
或StringBuffer
之间的实际性能折衷? (在我的情况下,它只是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上,编译器在编译时执行该操作。
- 在
method2
,String
连接相当于手动使用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比使用StringBuffer和StringBuilder要慢,
此外, StringBuilder比StringBuffer快,因为它不关心同步,(尽pipe看起来稍微快一些,如果我们增加迭代次数,速度会相对不同)。
现在,要决定使用哪个类,请考虑以下因素:
- 如果你正在创build一个不应该在程序stream程中被修改的静态string,那么就使用String对象。
- 如果你正在创build一个需要在多个线程之间共享的dynamicstring,那么考虑使用StringBuffer 。
- 如果两个因素都不存在(这是一个非常常见的情况),那就使用StringBuilder 。
来源: StringBuilder VS StringBuffer