给定下面的2个toString()实现,哪个是首选的:

public String toString(){
    return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}

or

public String toString(){
    StringBuilder sb = new StringBuilder(100);
    return sb.append("{a:").append(a)
          .append(", b:").append(b)
          .append(", c:").append(c)
          .append("}")
          .toString();
}

?

更重要的是,鉴于我们只有3个属性,它可能不会有什么不同,但在什么时候你会从+ concat切换到StringBuilder?


当前回答

这是我在Java8中检查的

Using String concatenation Using StringBuilder long time1 = System.currentTimeMillis(); usingStringConcatenation(100000); System.out.println("usingStringConcatenation " + (System.currentTimeMillis() - time1) + " ms"); time1 = System.currentTimeMillis(); usingStringBuilder(100000); System.out.println("usingStringBuilder " + (System.currentTimeMillis() - time1) + " ms"); private static void usingStringBuilder(int n) { StringBuilder str = new StringBuilder(); for(int i=0;i<n;i++) str.append("myBigString"); } private static void usingStringConcatenation(int n) { String str = ""; for(int i=0;i<n;i++) str+="myBigString"; }

如果对大量的字符串使用字符串连接,这真的是一场噩梦。

usingStringConcatenation 29321 ms
usingStringBuilder 2 ms

其他回答

使用最新版本的Java(1.8)的反汇编(javap -c)显示了编译器引入的优化。+以及sb.append()将生成非常相似的代码。然而,如果我们在for循环中使用+,检查行为将是值得的。

在for循环中使用+添加字符串

Java:

public String myCatPlus(String[] vals) {
    String result = "";
    for (String val : vals) {
        result = result + val;
    }
    return result;
}

ByteCode:(用于循环摘录)

12: iload         5
14: iload         4
16: if_icmpge     51
19: aload_3
20: iload         5
22: aaload
23: astore        6
25: new           #3                  // class java/lang/StringBuilder
28: dup
29: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload         6
38: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc          5, 1
48: goto          12

使用stringbuilder.append添加字符串

Java:

public String myCatSb(String[] vals) {
    StringBuilder sb = new StringBuilder();
    for(String val : vals) {
        sb.append(val);
    }
    return sb.toString();
}

ByteCdoe:(循环摘录)

17: iload         5
19: iload         4
21: if_icmpge     43
24: aload_3
25: iload         5
27: aaload
28: astore        6
30: aload_2
31: aload         6
33: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc          5, 1
40: goto          17
43: aload_2

不过,两者还是有一些明显的区别。在第一种情况下,使用了+,为每次for循环迭代创建新的StringBuilder,并通过执行toString()调用存储生成的结果(29到41)。所以当你在for循环中使用+运算符时,你会生成你真正不需要的中间字符串。

版本1更可取,因为它更短,而且编译器实际上会把它转换成版本2——没有任何性能差异。

更重要的是,我们只有3个 属性,它可能不会生成 不同,但在什么情况下 从concat转到builder?

当你在一个循环中连接时——那通常是编译器不能自己替换StringBuilder的时候。

对于像这样的简单字符串,我更喜欢使用

"string".concat("string").concat("string");

按照顺序,我认为构造字符串的首选方法是使用StringBuilder, string# concat(),然后是重载的+运算符。在处理大字符串时,StringBuilder显著提高了性能,就像使用+操作符会大幅降低性能一样(随着字符串大小的增加,性能会呈指数级下降)。使用.concat()的一个问题是它会抛出nullpointerexception。

我认为我们应该使用StringBuilder附加方法。 原因是:

String连接每次都会创建一个新的String对象(因为String是不可变对象),所以它将创建3个对象。 使用String生成器只会创建一个对象[StringBuilder is mutable],并且进一步的字符串被追加到它。

在Java 9中,版本1应该更快,因为它被转换为invokedynamic调用。更多细节可以在JEP-280中找到:

其思想是用对java.lang.invoke的简单invokedynamic调用替换整个StringBuilder追加舞蹈。StringConcatFactory,它将接受需要连接的值。