Java如何使用“+”来进行string连接?

我使用StringBuilder阅读了关于Java使用+=运算符的方式。
("a" + "b")操作是否一样?

不。使用StringBuilder与使用"a" + "b"

在Java中,String实例是不可变的。

所以,如果你这样做:

 String c = "a" + "b"; 

每次连接时都会创build新的string。

另一方面,StringBuilder就像一个缓冲区,可以在追加新的string时根据需要进行增长。

 StringBuilder c = new StringBuilder(); c.append("a"); c.append("b"); // c is only created once and appended "a" and "b". 

拇指规则(改变感谢我得到的意见):

如果要连接很多(即在一个循环内连接,或者生成由多个string连接的variables组成的大XML),请使用StringBuilder。 否则,简单的连接(使用+运算符)将会很好。

编译器优化在编译这种types的代码时也起着巨大的作用。

这里是关于这个话题的进一步解释。 更多关于这个问题的StackOVerflow问题:

一 , 二 , 三

如果将string(字面意思是"foo" + "bar" )组合在一起,编译器会在编译时执行它,而不是在运行时。

如果你有两个非文字string并且用+join,编译器(无论如何)都会使用一个StringBuilder ,但不一定是最有效的方式。 所以举个例子,如果你有这个:

 String repeat(String a, int count) { String rv; if (count <= 0) { return ""; } rv = a; while (--count > 0) { rv += a; } return rv; } 

Sun编译器将实际产生的字节码如下所示:

 String repeat(String a, int count) { String rv; if (count <= 0) { return ""; } rv = a; while (--count > 0) { rv = new StringBuilder().append(rv).append(a).toString(); } return rv; } 

(是的,真的 – 看到这个答案结束时的反汇编。)请注意,它在每次迭代中创build了一个新的StringBuilder ,然后将结果转换为String 。 由于所有的临时内存分配,这是效率低下的(但不要紧,除非你这么做):它分配一个StringBuilder及其缓冲区,很可能在第一次append重新分配缓冲区[if rv more长度超过16个字符,这是默认的缓冲区大小],如果不是第一个,那么几乎肯定是第二个append ,然后在最后分配一个String – 然后在下一次迭代中再次完成。

如有必要,您可以通过重写它来显式使用StringBuilder来获得效率:

 String repeat(String a, int count) { StringBuilder rv; if (count <= 0) { return ""; } rv = new StringBuilder(a.length() * count); while (count-- > 0) { rv.append(a); } return rv.toString(); } 

在那里,我们使用了一个显式的StringBuilder ,并且将它的初始缓冲区容量设置得足够大以保存结果。 这样更有记忆效率,但是对于没有经验的代码维护者来说,这当然不那么清楚,而且写起来更麻烦。 所以, 如果你发现一个紧凑的stringconcat循环的性能问题,这可能是解决它的一种方法。

你可以在下面的testing类中看到这个在下面的StringBuilder中的作用:

 public class SBTest { public static final void main(String[] params) { System.out.println(new SBTest().repeat("testing ", 4)); System.exit(0); } String repeat(String a, int count) { String rv; if (count <= 0) { return ""; } rv = a; while (--count > 0) { rv += a; } return rv; } } 

…这样反javap -c SBTest (使用javap -c SBTest ):

 Compiled from "SBTest.java" public class SBTest extends java.lang.Object{ public SBTest(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public static final void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: new #3; //class SBTest 6: dup 7: invokespecial #4; //Method "<init>":()V 10: ldc #5; //String testing 12: iconst_4 13: invokevirtual #6; //Method repeat:(Ljava/lang/String;I)Ljava/lang/String; 16: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 19: iconst_0 20: invokestatic #8; //Method java/lang/System.exit:(I)V 23: return java.lang.String repeat(java.lang.String, int); Code: 0: iload_2 1: ifgt 7 4: ldc #9; //String 6: areturn 7: aload_1 8: astore_3 9: iinc 2, -1 12: iload_2 13: ifle 38 16: new #10; //class java/lang/StringBuilder 19: dup 20: invokespecial #11; //Method java/lang/StringBuilder."<init>":()V 23: aload_3 24: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: aload_1 28: invokevirtual #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #13; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: astore_3 35: goto 9 38: aload_3 39: areturn } 

注意在循环的每次迭代中如何创build一个新的StringBuilder ,并使用默认的缓冲区容量创build。

所有这些临时分配的东西听起来都很难看,但是再一次,只有当你处理大量的循环和/或大量的string时。 而且,当生成的字节码运行时,JVM可能会进一步优化它。 Sun的HotSpot JVM,例如,是一个非常成熟的JIT优化编译器。 一旦确定循环为热点,就可能find重构它的方法。 或者不,当然。 🙂

我的经验法则是当我看到一个性能问题时,我担心它,或者如果我知道我正在进行大量的连接,这很可能是性能问题,并且从可维护性的angular度来看,代码不会受到显着的影响如果我使用StringBuilder来代替。 那些反对过早优化的狂热分子可能会不赞同我的第二个。 🙂

是的,这是相同的,但编译器可以在发布代码之前优化文字连接,所以"a"+"b"可以直接发布为"ab"

为了将一个expression式中固定数量的string连接到+ ,编译器将使用一个StringBuilder产生代码。

如线

 String d = a + b + c; 

导致与该行相同的字节码

 String d = new StringBuilder().append(a).append(b).append(c).toString(); 

当使用javac编译器进行编译时。 (Eclipse编译器通过调用new StringBuilder(a)生成更优化的代码,从而节省一个方法调用。)

正如其他答案中提到的那样,编译器会将string文字(如"a" + "b"成一个string本身,生成包含"ab"字节码。

正如在网上提到的那样,你不应该使用+在循环中build立一个string,因为你要将string的开始部分复制到新的string中。 在这种情况下,你应该使用一个你在循环之外声明的StringBuilder

"a" + "b"操作

虽然可读性强,容易格式化和直接,但用“+”连接string在Java中被认为是不好的。

每次通过“+”(String.concat())追加一些东西时,都会创build一个新的string,旧的string内容被复制,新的内容被追加,旧的string被丢弃。 string得到的时间越长,复制的次数越多,产生的垃圾越多。 注意:如果你只是连接几个(比如3,4个)string,而不是通过循环创build一个string,或者只是写一些testing应用程序,你仍然可以坚持“+”

使用StringBuilder

当执行大量的string操作(或通过循环追加)时,可能推荐使用StringBuilder .appendreplace“+”。 在“+”的情况下提到的中间对象在append()方法调用期间不会被创build。

另外需要注意的是,在Sun Java编译器中进行优化,当它看到string连接时会自动创buildStringBuildersStringBuffers <5.0)。 但那只是Sun Java编译器。

string更常用+运算符连接,如"Hello," + " world" + "!"

资源